Backend — FastAPI

FastAPI Framework

Modern async Python APIs with automatic validation, serialization, and Swagger docs from type hints. One of the fastest Python frameworks — built for APIs and ML model serving.

Async / ASGIPython 3.10+Auto Swagger
ASGIServer
PydanticValidation
OpenAPIAuto Docs
AsyncNative
High Perf
High-Performance APIs with Zero Boilerplate
FastAPI is built on Starlette (ASGI) and Pydantic (validation). Write standard Python with type hints — FastAPI handles validation, serialization, error messages, and generates interactive API docs automatically.
Request
JSON / Form / Query
Pydantic
Validate & Parse
Your Function
Clean Python objects
Blazing Performance
On par with Node.js and Go. Native async/await via Starlette ASGI — handles thousands of concurrent requests.
Automatic Docs
Swagger UI at /docs and ReDoc at /redoc — generated from your code, zero extra work.
Type Safety
Python type hints power validation, serialization, and IDE autocomplete. Catch bugs at definition time.
Dependency Injection
A clean DI system for sharing DB sessions, auth checks, and common logic across routes without repetition.
Standards-Based
Fully OpenAPI 3.0 and JSON Schema compliant. Auto-generate client SDKs in any language from your API.
ML Model Serving
The go-to choice for wrapping ML models as APIs. Used by Netflix, Uber, Microsoft, and countless AI teams.
2018
Year Released
Created by Sebastián Ramírez (tiangolo)
ASGI
Server Type
Async — vs WSGI (Flask/Django sync)
3rd
Most Starred
Python web framework on GitHub
~3×
Faster than Flask
Benchmarks on async I/O workloads
Quick Start — Up and Running in Minutes
Install FastAPI, Uvicorn (ASGI server), and write your first typed endpoint. The docs auto-appear the moment the server starts.
01
Install FastAPI & Uvicorn
Uvicorn is the ASGI server that runs FastAPI. The [standard] extra adds websocket and performance libraries.
bash
pip install fastapi uvicorn[standard] # For database support: pip install sqlalchemy databases asyncpg # For JWT auth: pip install python-jose[cryptography] passlib[bcrypt]
02
Your First FastAPI App
Notice how type hints do the work — item_id: int validates and converts automatically. No extra code needed.
main.py
from fastapi import FastAPI from pydantic import BaseModel app = FastAPI(title="My API", version="1.0.0") class Item(BaseModel): name: str price: float stock: int = 0 @app.get("/") def root(): return {"message": "Hello World"} @app.get("/items/{item_id}") def get_item(item_id: int): # int validated automatically return {"id": item_id} @app.post("/items", status_code=201) def create_item(item: Item): # body auto-parsed & validated return item
03
Run the Server
The --reload flag restarts on every file save. Visit /docs for instant interactive API documentation.
bash
# Development — auto-reload on save uvicorn main:app --reload # Production — multiple workers uvicorn main:app --host 0.0.0.0 --port 8000 --workers 4 # Then open: # http://localhost:8000/docs ← Swagger UI # http://localhost:8000/redoc ← ReDoc # http://localhost:8000/openapi.json ← Raw schema
04
Recommended Project Structure
For production apps, split into routers, schemas, models, and database modules.
myapi/ ├── main.py # app instance + routers ├── .env # secrets (DB URL, JWT secret) ├── database.py # engine, session, Base ├── models.py # SQLAlchemy ORM models ├── schemas.py # Pydantic request/response schemas ├── dependencies.py # get_db, get_current_user └── routers/ ├── users.py # /users endpoints ├── items.py # /items endpoints └── auth.py # /auth/login, /auth/refresh
Routing, Path & Query Parameters
FastAPI's routing is decorator-based like Flask, but type hints do all the validation and conversion work. No manual parsing needed.
01
Path Parameters with Validation
Declare path params in the route string and function signature. FastAPI validates and converts the type automatically — wrong type = 422 Unprocessable Entity.
python
from fastapi import FastAPI, Path from enum import Enum class ModelName(str, Enum): alexnet = "alexnet" resnet = "resnet" vgg = "vgg" # Simple path param with type validation @app.get("/users/{user_id}") def get_user(user_id: int): return {"id": user_id} # Enum param — only allows defined values @app.get("/models/{model_name}") def get_model(model_name: ModelName): return {"model": model_name, "value": model_name.value} # Path() adds constraints and docs metadata @app.get("/items/{item_id}") def get_item(item_id: int = Path(gt=0, description="Item ID must be positive")): return {"id": item_id}
02
Query Parameters & Optional Fields
Any function parameter not in the path string becomes a query parameter. Use Optional or a default of None to make it optional.
python
from typing import Optional from fastapi import Query # Required query param: GET /items?category=books @app.get("/search") def search(q: str): return {"query": q} # Optional with defaults: GET /items?skip=0&limit=20&active=true @app.get("/items") def list_items( skip: int = 0, limit: int = 20, active: Optional[bool] = None, q: Optional[str] = Query(None, min_length=3, max_length=50) ): return {"skip": skip, "limit": limit, "active": active, "q": q}
03
APIRouter — Organizing Routes
Split routes across files using APIRouter, then include them in the main app. Equivalent to Flask Blueprints.
routers/users.py
from fastapi import APIRouter router = APIRouter(prefix="/users", tags=["users"]) @router.get("/") def list_users(): # → GET /users/ return [] @router.get("/{user_id}") def get_user(user_id: int): # → GET /users/{user_id} return {"id": user_id} # In main.py: # from routers import users # app.include_router(users.router)
Pydantic — Validation & Serialization
Pydantic models define the shape of your request bodies and responses. They validate incoming data, throw clear error messages for bad input, and handle serialization to/from JSON automatically.
01
Request & Response Schemas
Define separate schemas for creating vs. reading objects. Use response_model to strip fields you don't want to expose (like passwords).
schemas.py
from pydantic import BaseModel, EmailStr, Field from typing import Optional from datetime import datetime # Schema for creating a user (incoming request) class UserCreate(BaseModel): username: str = Field(min_length=3, max_length=50) email: EmailStr # validates email format password: str = Field(min_length=8) # Schema for reading a user (response — no password!) class UserRead(BaseModel): id: int username: str email: str created: datetime class Config: from_attributes = True # allow ORM model → schema conversion # Use response_model to filter the output automatically @app.post("/users", response_model=UserRead, status_code=201) def create_user(user: UserCreate, db: Session = Depends(get_db)): # password never appears in the response — UserRead excludes it ...
02
Field Validation & Constraints
Use Field() for constraints and metadata. Validators run custom logic. FastAPI surfaces all validation errors as a 422 response with clear messages.
python
from pydantic import BaseModel, Field, field_validator class Product(BaseModel): name: str = Field(min_length=1, max_length=200, description="Product name") price: float = Field(gt=0, description="Must be positive") discount: float = Field(default=0.0, ge=0, le=1) # 0% to 100% tags: list[str] = [] @field_validator('name') @classmethod def name_must_not_be_blank(cls, v: str) -> str: if not v.strip(): raise ValueError('name cannot be blank') return v.strip()
03
Nested Models & Lists
Pydantic models compose naturally. FastAPI handles nested validation and generates accurate JSON Schema for the docs.
python
class Address(BaseModel): street: str city: str zip: str class UserProfile(BaseModel): name: str address: Address # nested model friends: list[str] = [] # list of strings # FastAPI accepts + validates this JSON automatically: # { # "name": "Omer", # "address": { "street": "MG Road", "city": "Hyderabad", "zip": "500001" }, # "friends": ["Alice", "Bob"] # }
Async & Await — Native Concurrency
FastAPI is built on ASGI and handles async natively. Async routes let the server handle other requests while waiting for I/O — database queries, HTTP calls, file reads — instead of blocking the whole process.
01
async def vs def Routes
Use async def when your route does I/O (DB query, HTTP request, file read). Use regular def for CPU-bound work — FastAPI runs these in a thread pool automatically.
python
import httpx # Async route — awaits I/O without blocking the server @app.get("/weather/{city}") async def get_weather(city: str): async with httpx.AsyncClient() as client: res = await client.get(f"https://api.weather.com/{city}") return res.json() # Regular def — runs in threadpool, won't block async loop @app.get("/compute") def heavy_compute(): result = some_cpu_intensive_function() return {"result": result}
02
Background Tasks
Run work after returning the response — sending emails, writing logs, processing uploads — without making the client wait.
python
from fastapi import BackgroundTasks def send_welcome_email(email: str): # Runs after the response is sent — client doesn't wait send_email(to=email, subject="Welcome!", body="Thanks for signing up.") @app.post("/register") async def register(user: UserCreate, background_tasks: BackgroundTasks): db_user = create_user_in_db(user) background_tasks.add_task(send_welcome_email, user.email) return {"message": "Registered! Welcome email sent."}
03
Startup & Shutdown Events
Run code when the application starts or shuts down — connect to databases, load ML models, clean up resources.
python
from contextlib import asynccontextmanager @asynccontextmanager async def lifespan(app: FastAPI): # Startup — runs once when server starts print("Loading ML model...") app.state.model = load_ml_model() yield # Shutdown — runs when server stops print("Cleaning up...") app.state.model = None app = FastAPI(lifespan=lifespan)
FastAPI is the go-to choice for serving ML models because async I/O keeps the server responsive while model inference runs. Libraries like Hugging Face and LangChain have native FastAPI integration examples.
Dependency Injection
FastAPI's DI system is one of its best features. Declare a function as a dependency with Depends() — FastAPI calls it, resolves its own dependencies, and injects the result into your route. Clean, testable, composable.
01
Basic Dependencies
Share common query parameters, pagination logic, or any computed value across multiple routes.
python
from fastapi import Depends # Reusable pagination dependency def paginate(page: int = 1, size: int = 20) -> dict: return {"skip": (page - 1) * size, "limit": size} # Inject into any route — no code duplication @app.get("/users") def list_users(p: dict = Depends(paginate)): return db.query(User).offset(p["skip"]).limit(p["limit"]).all() @app.get("/items") def list_items(p: dict = Depends(paginate)): return db.query(Item).offset(p["skip"]).limit(p["limit"]).all()
02
Database Session Dependency
The most common dependency pattern — open a DB session per request, yield it, then close it whether or not an exception occurred.
dependencies.py
from database import SessionLocal from sqlalchemy.orm import Session def get_db() -> Session: db = SessionLocal() try: yield db # inject into the route finally: db.close() # always close, even on errors # Every route gets its own session — no leaks @app.get("/users/{uid}") def get_user(uid: int, db: Session = Depends(get_db)): return db.query(User).filter(User.id == uid).first()
03
Router-Level Dependencies
Apply a dependency to all routes in a router — e.g., require authentication for every endpoint under /admin.
python
from fastapi import APIRouter, Depends from dependencies import require_admin # Every route in this router requires admin auth admin_router = APIRouter( prefix="/admin", tags=["admin"], dependencies=[Depends(require_admin)] ) @admin_router.get("/users") def list_all_users(db: Session = Depends(get_db)): return db.query(User).all() # require_admin runs automatically
JWT Authentication
FastAPI has no built-in auth, but its DI system makes JWT implementation clean and reusable. Protect any route by adding a single Depends(get_current_user).
01
Password Hashing & Token Generation
Use passlib for password hashing and python-jose for JWT creation and verification.
auth.py
from datetime import datetime, timedelta from jose import jwt, JWTError from passlib.context import CryptContext SECRET_KEY = "your-secret-key-from-env" ALGORITHM = "HS256" TOKEN_EXP = 30 # minutes pwd_context = CryptContext(schemes=["bcrypt"]) def hash_password(password: str) -> str: return pwd_context.hash(password) def verify_password(plain: str, hashed: str) -> bool: return pwd_context.verify(plain, hashed) def create_access_token(data: dict) -> str: payload = data.copy() payload["exp"] = datetime.utcnow() + timedelta(minutes=TOKEN_EXP) return jwt.encode(payload, SECRET_KEY, algorithm=ALGORITHM)
02
Login Endpoint & Token Response
OAuth2PasswordRequestForm is a built-in FastAPI form that accepts username and password — standard OAuth2 format.
routers/auth.py
from fastapi import APIRouter, Depends, HTTPException, status from fastapi.security import OAuth2PasswordRequestForm auth_router = APIRouter(tags=["auth"]) @auth_router.post("/login") def login( form: OAuth2PasswordRequestForm = Depends(), db: Session = Depends(get_db) ): user = db.query(User).filter(User.email == form.username).first() if not user or not verify_password(form.password, user.password): raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid credentials" ) token = create_access_token({"sub": str(user.id)}) return {"access_token": token, "token_type": "bearer"}
03
Protected Routes with get_current_user
A single dependency that reads and validates the JWT from the Authorization header. Inject it into any route to protect it.
dependencies.py
from fastapi.security import OAuth2PasswordBearer oauth2_scheme = OAuth2PasswordBearer(tokenUrl="login") def get_current_user( token: str = Depends(oauth2_scheme), db: Session = Depends(get_db) ) -> User: try: payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM]) user_id = int(payload.get("sub")) except (JWTError, ValueError): raise HTTPException(status_code=401, detail="Invalid token") user = db.query(User).get(user_id) if not user: raise HTTPException(status_code=401, detail="User not found") return user # Protect any route with one line: @app.get("/me", response_model=UserRead) def get_me(current_user: User = Depends(get_current_user)): return current_user
Database with SQLAlchemy
FastAPI works with any database. SQLAlchemy is the standard choice. Define models in models.py, Pydantic schemas in schemas.py, and keep them separate — a key FastAPI pattern.
01
Database Setup
Create the engine, session factory, and declarative base. All models inherit from Base.
database.py
from sqlalchemy import create_engine from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import sessionmaker import os DATABASE_URL = os.environ.get("DATABASE_URL", "sqlite:///./dev.db") engine = create_engine(DATABASE_URL) SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) Base = declarative_base() # Create all tables (use Alembic for production migrations) # Base.metadata.create_all(bind=engine)
02
SQLAlchemy Models vs Pydantic Schemas
Keep these separate. SQLAlchemy models talk to the DB. Pydantic schemas validate API input/output. Use from_attributes = True to convert between them.
models.py
from sqlalchemy import Column, Integer, String, DateTime, ForeignKey from sqlalchemy.orm import relationship from database import Base from datetime import datetime class User(Base): __tablename__ = "users" id = Column(Integer, primary_key=True, index=True) email = Column(String, unique=True, nullable=False) password = Column(String, nullable=False) created = Column(DateTime, default=datetime.utcnow) posts = relationship("Post", back_populates="author")
03
CRUD in Routes
Combine the DB session dependency with Pydantic schemas for clean, type-safe database routes.
routers/users.py
from fastapi import APIRouter, Depends, HTTPException from sqlalchemy.orm import Session router = APIRouter(prefix="/users", tags=["users"]) @router.get("/", response_model=list[UserRead]) def list_users(skip: int = 0, limit: int = 20, db: Session = Depends(get_db)): return db.query(User).offset(skip).limit(limit).all() @router.post("/", response_model=UserRead, status_code=201) def create_user(user: UserCreate, db: Session = Depends(get_db)): if db.query(User).filter(User.email == user.email).first(): raise HTTPException(400, "Email already registered") db_user = User(email=user.email, password=hash_password(user.password)) db.add(db_user) db.commit() db.refresh(db_user) return db_user
Automatic Interactive API Documentation
FastAPI generates full API documentation from your code — no YAML files, no separate doc writing. Every endpoint, parameter, schema, and response is documented automatically.
01
Swagger UI at /docs
Interactive docs where you can test every endpoint directly in the browser — fill in parameters, send requests, see responses. Auto-generated from your route definitions.
bash
# Available automatically when server runs: http://localhost:8000/docs # Swagger UI — interactive http://localhost:8000/redoc # ReDoc — cleaner, good for sharing http://localhost:8000/openapi.json # Raw OpenAPI 3.0 schema
02
Enriching the Docs
Add descriptions, examples, and metadata to your API, routes, and schemas — all appear in the generated docs.
python
# App-level metadata app = FastAPI( title="My API", description="A full-featured API built with FastAPI", version="1.0.0", contact={"name": "Omer", "email": "omer@example.com"}, ) # Route-level metadata @app.get("/items/{item_id}", summary="Get item by ID", description="Returns a single item. Raises 404 if not found.", response_description="The item object", tags=["items"]) def get_item(item_id: int): ... # Schema-level examples class Item(BaseModel): name: str = Field(example="Laptop") price: float = Field(example=999.99)
03
Disable Docs in Production (Optional)
You may want to disable public docs in production for security or to avoid exposing your API structure.
python
import os # Disable docs in production, keep in development app = FastAPI( docs_url="/docs" if os.getenv("ENV") != "production" else None, redoc_url="/redoc" if os.getenv("ENV") != "production" else None, )
The OpenAPI schema at /openapi.json can be fed into tools like openapi-generator to auto-generate TypeScript, Kotlin, or Swift client SDKs — zero manual API client code.
FastAPI Quick-Reference Cheatsheet
Essential imports, patterns, and CLI commands in one place.
python — essential imports
from fastapi import ( FastAPI, APIRouter, Depends, HTTPException, status, Path, Query, Body, Header, BackgroundTasks ) from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm from pydantic import BaseModel, Field, EmailStr, field_validator from sqlalchemy.orm import Session from typing import Optional, Annotated from jose import jwt, JWTError from passlib.context import CryptContext
Pattern / CommandWhat It Does
FastAPI(title, version)Create the app instance with metadata
@app.get / post / put / delete / patchRegister an HTTP route
response_model=SchemaFilter and validate the response shape
status_code=201Set custom HTTP status on success
Depends(func)Inject a dependency into a route
HTTPException(status_code, detail)Return an HTTP error response
Path(gt=0)Add constraints to a path parameter
Query(None, min_length=3)Add constraints to a query parameter
Field(min_length=1, example="x")Add constraints and docs to a Pydantic field
app.include_router(router, prefix="/v1")Mount an APIRouter into the app
uvicorn main:app --reloadStart dev server with auto-reload
uvicorn main:app --workers 4Start production server with 4 workers
Test Your FastAPI Knowledge
5 questions covering the key concepts — type hints, Pydantic, async, DI, and auto docs.
1If you declare a route parameter as item_id: int but the client sends /items/abc, what does FastAPI return?
2What is the purpose of response_model=UserRead on a route that returns a SQLAlchemy User object?
3Which ASGI server is the standard choice for running FastAPI in production?
4In the get_db() dependency, why does it use yield instead of return?
5Where does FastAPI automatically generate interactive API documentation?
6Pydantic models in FastAPI primarily provide…
7FastAPI dependency injection uses…
8When should you use async def route handlers?
9OpenAPI schema in FastAPI is…
10FastAPI vs Flask — common trade-off?
Keep practicing!