Lesson 3 — Beginner

Building APIs with FastAPI

Ab tum server ki taraf khade ho. FastAPI tumse sirf function ka signature maangta hai — baaki validation, serialization aur documentation khud nikaal leta. Is lesson mein routes aur parameters, Pydantic models, dependency injection, sahi status codes aur clean errors, async mein external calls, startup/config/middleware, aur TestClient se endpoint testing dekhoge.

⏱ 30 min• Beginner• The Web: HTTP, REST & JSON

FastAPI kyun — aur kaise kaam karta hai

WHAT

FastAPI ek Python web framework hai jo type hints se poora API layer derive karta hai — request validate, response serialize, OpenAPI docs sab automatic. ASGI server (uvicorn) par chalta hai aur async-first hai.

WHEN

Jab bhi JSON API chahiye — backend for web/mobile, LLM wrapper service, internal microservice. Speed, validation aur free docs chahiye toh FastAPI natural choice hai.

WHERE

Tumhara har AI service isi tarah banegi: prompt endpoint, RAG service, webhook receiver. Is course ke aage ke levels mein bhi FastAPI hi rahega.

Core idea: function ka signature hi API contract hai. Path parameter, query parameter, request body, return type — sab wahi se dikhte hain. Ek jagah likho, teen kaam (validate, serialize, document) automatic ho jaate hain.

Free bonus: Server chalate hi /docs pe interactive Swagger UI aur /redoc pe formatted docs milte hain — API kisi ko explain karne ke liye alag se kuch nahi likhna padta.

Routes, path/query parameters aur bodies

from fastapi import FastAPI, Query
from pydantic import BaseModel

app = FastAPI()

class OrderIn(BaseModel):
    item: str
    qty: int = 1

@app.get("/orders/{order_id}")            # path parameter
def get_order(order_id: int, verbose: bool = False):   # query parameter
    return {"id": order_id, "verbose": verbose}

@app.get("/orders")                       # list + filters
def list_orders(status: str = Query("pending"), page: int = 1):
    return {"status": status, "page": page}

@app.post("/orders", status_code=201)     # body + custom status
def create_order(order: OrderIn):
    return {"id": 981, **order.model_dump()}
Trap: Path ka {order_id} aur function parameter ka order_id same naam ke hone chahiye — warna FastAPI ko parameter hi nahi milta (422/500). Naam rakhne ka ek hi rule hai: match karo.

Pydantic — validation jo khud likhne ki zarurat nahi

from pydantic import BaseModel, Field

class UserIn(BaseModel):
    name: str = Field(min_length=1, max_length=80)
    email: str
    age: int = Field(default=18, ge=0, le=120)

# Aise payload par FastAPI khud 422 + structured error body bhejta hai:
# {"name": "", "email": "not-an-email", "age": 500}

# Output model alag rakho — andar ki har field bahar nahi jaati
class UserOut(BaseModel):
    name: str
    email: str

Validation ka rule: input model (UserIn) aur output model (UserOut) alag rakho. Andar ke har field ko bahar bhejna zaroori nahi — password hash, internal flags, DB ids kabhi response mein nahi jaane chahiye. Response model wahi chhaanta hai jo dikhana hai.

Kya hota hai automatically: Missing field, galat type, constraint fail (min_length/ge/le) — teeno cases mein FastAPI 422 de deta hai jismein field-wise error hota hai. Tum manually if-else likhne ki zarurat hi nahi.

Dependency injection — reusable pieces

from fastapi import Depends, FastAPI, Header, HTTPException

# app = FastAPI() pehle se bana hai

def current_user(authorization: str = Header(default="")):
    if not authorization.startswith("Bearer "):
        raise HTTPException(status_code=401, detail="Missing token")
    return parse_token(authorization)      # tumhara token parsing logic

def get_db():
    conn = connect_database()              # tumhara connection logic
    try:
        yield conn                          # use ke liye do
    finally:
        conn.close()                        # kaam khatam, band karo

@app.get("/me")
def me(user = Depends(current_user), db = Depends(get_db)):
    return {"user": user}
Rule: Jo cheez har route ko chahiye (auth, db, settings), wo function ke andar copy-paste mat karo — dependency banao. Ek jagah badlo, sab jagah badal jaayega.

Status codes aur clean exception handling

from fastapi import FastAPI, HTTPException, Request
from fastapi.responses import JSONResponse

# app = FastAPI() pehle se hai

@app.get("/orders/{order_id}")
def get_order(order_id: int):
    order = find_order(order_id)
    if order is None:
        raise HTTPException(status_code=404, detail="Order not found")
    return order

# Unexpected errors ko bhi clean JSON mein badlo
@app.exception_handler(ValueError)
def value_error_handler(request: Request, exc: ValueError):
    return JSONResponse(status_code=400, content={"error": str(exc), "code": "bad_input"})

# Consistent shape har error mein: {"error": "...", "code": "..."}
Status code recipe: 400 = client ne galat bheja (format), 401 = token nahi, 403 = permission nahi, 404 = nahi mila, 409 = state clash, 422 = validation fail, 429 = rate limit, 500 = bug, 504 = upstream timeout. Yehi list agle 100 APIs mein bhi rahegi.

Async endpoints aur external services

import httpx
from fastapi import Depends, FastAPI, HTTPException

# app = FastAPI(lifespan=lifespan) — client lifespan mein bana hai (next section)

async def get_client():
    return app.state.http       # lifespan mein bana hua AsyncClient

@app.get("/weather/{city}")
async def weather(city: str, client: httpx.AsyncClient = Depends(get_client)):
    try:
        r = await client.get(
            "https://api.example.com/weather",
            params={"city": city},
            timeout=10.0,
        )
    except httpx.TimeoutException:
        raise HTTPException(status_code=504, detail="Upstream timeout")
    r.raise_for_status()
    return r.json()
Decision rule: Andar sirf I/O hai → async def + await. Saathi async library nahi hai (purana SDK) → def rakho aur heavy processing ko background task ya queue mein bhejo. Dono ka farq sirf tab dikhta hai jab do users ek saath aate hain.

Startup, config aur middleware

import os
from contextlib import asynccontextmanager

import httpx
from fastapi import FastAPI, Request

@asynccontextmanager
async def lifespan(app: FastAPI):
    app.state.http = httpx.AsyncClient(base_url=os.environ["UPSTREAM_URL"])
    print("startup: resources ready")
    yield
    await app.state.http.aclose()
    print("shutdown: resources closed")

app = FastAPI(lifespan=lifespan)

@app.middleware("http")
async def add_request_id(request: Request, call_next):
    response = await call_next(request)
    response.headers["X-Request-Id"] = request.headers.get("X-Request-Id", "generated-123")
    return response
Separation rule: Jo cheez har request ko chahiye wo Depends se, jo app ke liye zaroori hai wo lifespan se, jo environment ki hai wo env vars se. Teenon ko alag rakho — tabhi config badalne par code nahi badalna padta.

Endpoints ka testing — TestClient

from fastapi.testclient import TestClient

client = TestClient(app)   # with TestClient(app) likho toh lifespan bhi chalta hai

def test_create_order():
    r = client.post("/orders", json={"item": "gpu-hours", "qty": 2})
    assert r.status_code == 201
    assert r.json()["qty"] == 2

def test_missing_field_is_422():
    r = client.post("/orders", json={"item": "gpu-hours"})
    assert r.status_code == 422

def test_missing_token_is_401():
    r = client.get("/me")
    assert r.status_code == 401

# chalao: uv run pytest -q
TDD-lite rule: Naya endpoint = pehle ek failing test. Test likhne se pehle sochna padta hai ki success kya hai aur failure kya — wahi asli design hai, code toh uske baad aata hai.

Exercise

Question: FastAPI ki automatically generated interactive docs ka default path kya hai?

Question: Request body validate karne ke liye jo library use hoti hai uska naam?

Question: Async endpoint mein external HTTP calls ke liye requests ki jagah kaunsi async library use karo?

Question: Dependency (auth, db) inject karne ke liye FastAPI mein kaunsa function use hota hai?

Common mistakes

Desi example: Thali ka order-pad — FastAPI

Restaurant ka order pad (route) sab kuch set karta hai — waiter har dish ka valid portion likhta hai (Pydantic validation), galat order par standard reply jaati hai (status codes), aur menu khud chhapa rehta hai (/docs). Menu dekh kar hi pata chal jaata hai kya milta hai:

python
from fastapi import FastAPI, HTTPException

app = FastAPI()

MENU = {"paneer": 220, "dal": 120}

@app.get("/menu")
def menu():
    return MENU                      # dict -> JSON automatic

@app.get("/dish/{name}")
def dish(name: str):
    if name not in MENU:
        raise HTTPException(status_code=404, detail="Nahi hai")
    return {"name": name, "price": MENU[name]}

# Chalao: uv run uvicorn main:app --reload  ->  /docs kholo
Interview angle — yahan se sawaal aate hain
  • Q: FastAPI mein validation kahan se aati hai — tum manually check kyun nahi karte? — Pydantic models se, framework khud 422 deta hai.
  • Q: 422 aur 400 mein farq kya hai? — 422 body parse ho gayi par schema/validation fail, 400 format/contract hi galat hai.
  • Q: async endpoint mein requests library kyun galat hai? — wo sync hai, event loop block hota hai; httpx AsyncClient use karo.
  • Q: Dependency injection ka practical faayda? — auth/db logic ek jagah, reuse across routes, tests mein easy override.
  • Q: TestClient se endpoint test kaise karte hain? — TestClient(app) se request, status_code + json() par assertions.
Lesson complete?

FastAPI par API banana aaya. Agla lesson — Data & Persistence Basics — mein service ka data restart ke baad bhi zinda rakhne ke liye PostgreSQL se jodenge.

Popular courses: Data Science Course in Lucknow · AI Course in Lucknow · Python Course in Lucknow · Power BI Course · Data Analytics Course