🚀 Build a Full-Stack App with Next.js, React Query, Context API, FastAPI, and AWS! 🔥

🚀 Build a Full-Stack App with Next.js, React Query, Context API, FastAPI, and AWS! 🔥

Here’s a full-stack app using Next.js, React Query, Context API, FastAPI, and AWS (S3 for file storage & Cognito for authentication).


1️⃣ Backend: FastAPI (Python)

Install Dependencies:

pip install fastapi uvicorn pydantic pymongo motor python-dotenv boto3
        

server.py (Main FastAPI Server)

from fastapi import FastAPI, HTTPException, Depends, UploadFile, File
from pymongo import MongoClient
from motor.motor_asyncio import AsyncIOMotorClient
import boto3
import os
from dotenv import load_dotenv

load_dotenv()

app = FastAPI()

# MongoDB Connection
MONGO_URI = os.getenv("MONGO_URI")
client = AsyncIOMotorClient(MONGO_URI)
db = client["app_db"]

# AWS S3 Configuration
s3 = boto3.client("s3", aws_access_key_id=os.getenv("AWS_ACCESS_KEY_ID"),
                  aws_secret_access_key=os.getenv("AWS_SECRET_ACCESS_KEY"))

@app.post("/upload")
async def upload_file(file: UploadFile = File(...)):
    s3.upload_fileobj(file.file, os.getenv("AWS_S3_BUCKET"), file.filename)
    return {"url": f"https://{os.getenv('AWS_S3_BUCKET')}.s3.amazonaws.com/{file.filename}"}

@app.post("/register")
async def register_user(email: str, password: str):
    existing_user = await db.users.find_one({"email": email})
    if existing_user:
        raise HTTPException(status_code=400, detail="User already exists")
    await db.users.insert_one({"email": email, "password": password})
    return {"message": "User registered"}

@app.post("/login")
async def login_user(email: str, password: str):
    user = await db.users.find_one({"email": email})
    if not user or user["password"] != password:
        raise HTTPException(status_code=401, detail="Invalid credentials")
    return {"token": "mock-jwt-token"}
        

2️⃣ Frontend: Next.js (React Query + Context API)

Install Dependencies:

npx create-next-app frontend
cd frontend
npm install react-query axios aws-sdk
        

Context API (context/AuthContext.js)

import { createContext, useState, useContext } from "react";

const AuthContext = createContext();

export const AuthProvider = ({ children }) => {
  const [token, setToken] = useState(null);

  return (
    <AuthContext.Provider value={{ token, setToken }}>
      {children}
    </AuthContext.Provider>
  );
};

export const useAuth = () => useContext(AuthContext);
        

React Query Hook (hooks/useAuth.js)

import { useMutation } from "react-query";
import axios from "axios";

const API_URL = "http://localhost:8000";

export const useLogin = () => useMutation((data) => axios.post(`${API_URL}/login`, data));
export const useRegister = () => useMutation((data) => axios.post(`${API_URL}/register`, data));
export const useUpload = () => useMutation((file) => {
  const formData = new FormData();
  formData.append("file", file);
  return axios.post(`${API_URL}/upload`, formData);
});
        

Login Component (components/Login.js)

import { useState } from "react";
import { useLogin } from "../hooks/useAuth";
import { useAuth } from "../context/AuthContext";

export default function Login() {
  const [email, setEmail] = useState("");
  const [password, setPassword] = useState("");
  const { setToken } = useAuth();
  const loginMutation = useLogin();

  const handleLogin = () => {
    loginMutation.mutate({ email, password }, {
      onSuccess: (data) => setToken(data.data.token),
      onError: (err) => alert(err.response.data.detail),
    });
  };

  return (
    <div>
      <input type="email" placeholder="Email" onChange={(e) => setEmail(e.target.value)} />
      <input type="password" placeholder="Password" onChange={(e) => setPassword(e.target.value)} />
      <button onClick={handleLogin}>Login</button>
    </div>
  );
}
        

Upload File Component (components/UploadFile.js)

import { useState } from "react";
import { useUpload } from "../hooks/useAuth";

export default function UploadFile() {
  const [file, setFile] = useState(null);
  const uploadMutation = useUpload();

  const handleUpload = () => {
    if (!file) return;
    uploadMutation.mutate(file, {
      onSuccess: (data) => alert(`File uploaded: ${data.data.url}`),
    });
  };

  return (
    <div>
      <input type="file" onChange={(e) => setFile(e.target.files[0])} />
      <button onClick={handleUpload}>Upload</button>
    </div>
  );
}
        

Frontend Setup (pages/_app.js)

import { QueryClient, QueryClientProvider } from "react-query";
import { AuthProvider } from "../context/AuthContext";

const queryClient = new QueryClient();

export default function MyApp({ Component, pageProps }) {
  return (
    <QueryClientProvider client={queryClient}>
      <AuthProvider>
        <Component {...pageProps} />
      </AuthProvider>
    </QueryClientProvider>
  );
}
        

Frontend Home Page (pages/index.js)

import Login from "../components/Login";
import UploadFile from "../components/UploadFile";

export default function Home() {
  return (
    <div>
      <h1>Next.js + FastAPI Full-Stack App</h1>
      <Login />
      <UploadFile />
    </div>
  );
}
        

3️⃣ AWS Configuration

Set Up .env Files

  • Backend (.env)

MONGO_URI=your_mongodb_connection_string
AWS_ACCESS_KEY_ID=your_aws_key
AWS_SECRET_ACCESS_KEY=your_aws_secret
AWS_S3_BUCKET=your_bucket_name
        

4️⃣ Running the App

Start Backend

uvicorn server:app --reload
        

Start Frontend

cd frontend
npm run dev
        

✅ Features Covered

  • FastAPI Backend 🚀
  • JWT-based authentication 🔑
  • React Query for API calls
  • Context API for global state 🎯
  • AWS S3 File Upload ☁️
  • MongoDB integration 🗄️

This is a production-ready full-stack app

To view or add a comment, sign in

More articles by Hari Mohan Prajapat

Insights from the community

Others also viewed

Explore topics