Project 1~45 minutesWorked Example
Hello Full-Stack
Interview Scenario: "Let's start simple. Can you set up a basic full-stack app where the frontend displays a greeting message fetched from the backend?"
What You'll Learn
- Project structure for both Next.js and FastAPI
- CORS configuration (critical!)
- Basic fetch() from React to Python
- The useEffect + useState pattern for data fetching
Loading...
Step 1: The FastAPI Backend
If you followed the setup guide, you already have a backend folder. Let's expand it:
backend/main.py
1from fastapi import FastAPI2from fastapi.middleware.cors import CORSMiddleware34app = FastAPI()56# THIS IS CRITICAL - without this, your frontend cannot call your backend7app.add_middleware(8 CORSMiddleware,9 allow_origins=["http://localhost:3000"], # Your Next.js app10 allow_credentials=True,11 allow_methods=["*"], # Allow all HTTP methods12 allow_headers=["*"], # Allow all headers13)1415@app.get("/")16def read_root():17 return {"message": "Hello from FastAPI!"}1819@app.get("/api/greeting/{name}")20def get_greeting(name: str):21 return {"greeting": f"Hello, {name}! Welcome to full-stack development."}
CORS is the #1 gotcha
Always add CORS middleware IMMEDIATELY after creating your FastAPI app. Forgetting CORS is the most common reason full-stack apps fail to connect. If asked "why isn't my frontend getting data?", CORS should be your first thought.
Run the backend:
Terminal 1
1uvicorn main:app --reload --port 800023# Visit http://localhost:8000/docs to see auto-generated API docs!
Step 2: The Next.js Frontend
frontend/app/page.tsx
1"use client" // THIS IS REQUIRED for useState/useEffect23import { useState, useEffect } from "react"45export default function Home() {6 const [greeting, setGreeting] = useState<string>("")7 const [loading, setLoading] = useState<boolean>(true)8 const [name, setName] = useState<string>("")910 // Fetch initial greeting on page load11 useEffect(() => {12 fetch("http://localhost:8000/")13 .then(res => res.json())14 .then(data => {15 setGreeting(data.message)16 setLoading(false)17 })18 .catch(err => {19 setGreeting("Error connecting to backend")20 setLoading(false)21 })22 }, []) // Empty array = run once on mount2324 // Fetch personalized greeting25 const fetchPersonalGreeting = async () => {26 if (!name) return27 const res = await fetch(`http://localhost:8000/api/greeting/${name}`)28 const data = await res.json()29 setGreeting(data.greeting)30 }3132 return (33 <main className="min-h-screen p-8">34 <h1 className="text-3xl font-bold mb-4">Full-Stack Hello</h1>3536 {loading ? (37 <p>Loading...</p>38 ) : (39 <p className="text-xl mb-6">{greeting}</p>40 )}4142 <div className="flex gap-2">43 <input44 type="text"45 value={name}46 onChange={(e) => setName(e.target.value)}47 placeholder="Enter your name"48 className="border p-2 rounded"49 />50 <button51 onClick={fetchPersonalGreeting}52 className="bg-blue-500 text-white px-4 py-2 rounded hover:bg-blue-600"53 >54 Get Personal Greeting55 </button>56 </div>57 </main>58 )59}
Understanding the Code
"use client" directive
Next.js 13+ uses Server Components by default. Adding
"use client" makes the component run in the browser, which is required for useState, useEffect, and event handlers like onClick.useEffect with empty dependency array []
This pattern means "run this code once when the component first appears." Perfect for initial data fetching. If you put a variable in the array like
[userId], it re-runs whenever that variable changes.The loading state pattern
Always track loading state. Users should never see a blank screen. This is a small detail that shows interviewers you think about user experience.
Exercises
What You Learned
FastAPI
- Basic route structure (@app.get)
- Path parameters (/greeting/{name})
- CORS configuration
Next.js/React
- 'use client' directive
- useState for component state
- useEffect for side effects
- Basic fetch() with async/await
Ready for more?
In the next project, you'll build a full CRUD application with a real database.
Project 2: Task Tracker