FastAPI Basics
10 examples to get you started with FastAPI - 7 basic and 3 intermediate.
Prerequisites
uv venv && source .venv/bin/activate
uv pip install "fastapi[standard]>=0.115" uvicorn httpx- FastAPI 0.115+ on Python 3.14 uses Pydantic 2 for validation.
- Start the dev server with
fastapi dev main.py.
Basic Examples
1. Hello World API
Minimal app with automatic OpenAPI docs.
from fastapi import FastAPI
app = FastAPI(title="Demo API")
@app.get("/")
def read_root() -> dict[str, str]:
return {"message": "Hello, FastAPI"}FastAPI()creates the ASGI application object.- Return type hints appear in the generated schema at
/docs. - Run with
uvicorn main:app --reloadorfastapi dev main.py.
Related: Request & Response Models - typed request bodies
2. Path Parameters
Capture typed values from the URL path.
from fastapi import FastAPI, HTTPException
app = FastAPI()
@app.get("/users/{user_id}")
def get_user(user_id: int) -> dict[str, int]:
if user_id < 1:
raise HTTPException(status_code=404, detail="User not found")
return {"user_id": user_id}- Path parameter names must match the route template.
- FastAPI coerces types and returns 422 on invalid input.
- Use
HTTPExceptionfor explicit 4xx responses.
Related: Middleware & Error Handling - consistent error bodies
3. Query Parameters
Optional filters and pagination from the query string.
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items")
def list_items(
skip: int = Query(0, ge=0),
limit: int = Query(20, ge=1, le=100),
q: str | None = None,
) -> dict[str, object]:
return {"skip": skip, "limit": limit, "q": q}Query()adds validation metadata beyond a plain default.- Optional parameters use
str | None = None. - Constraints like
geandleappear in OpenAPI.
4. JSON Request Body
POST JSON validated by Pydantic 2 models.
from fastapi import FastAPI
from pydantic import BaseModel, Field
app = FastAPI()
class ItemCreate(BaseModel):
name: str = Field(min_length=1, max_length=100)
price: float = Field(gt=0)
@app.post("/items", status_code=201)
def create_item(item: ItemCreate) -> ItemCreate:
return item- The body type is inferred from the parameter annotation.
- Validation failures return 422 with field-level
detail. - Use separate input and output models at API boundaries.
Related: Request & Response Models - response_model filtering
5. Response Model Filtering
Hide internal fields from serialized responses.
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class UserIn(BaseModel):
username: str
password: str
class UserOut(BaseModel):
username: str
@app.post("/users", response_model=UserOut)
def create_user(user: UserIn) -> UserIn:
return userresponse_modelcontrols both OpenAPI and the JSON payload.- Never return ORM objects without an explicit response schema.
- Passwords must never appear in response models.
6. Automatic OpenAPI Docs
Interactive Swagger UI ships with every app.
from fastapi import FastAPI
app = FastAPI(
title="Inventory API",
version="1.0.0",
docs_url="/docs",
redoc_url="/redoc",
)
@app.get("/health")
def health() -> dict[str, str]:
return {"status": "ok"}- Visit
/docsfor Swagger UI and/redocfor ReDoc. - Disable docs in production with
docs_url=Nonewhen policy requires. - Route docstrings and tags enrich the generated spec.
7. Status Codes and Headers
Set HTTP semantics on success responses.
from fastapi import FastAPI, Response
app = FastAPI()
@app.post("/notify", status_code=202)
def enqueue(response: Response) -> dict[str, str]:
response.headers["X-Job-Id"] = "job-42"
return {"status": "accepted"}- Use 201 for creates, 202 for async acceptance, 204 for empty bodies.
- Inject
Responseto set headers without changing the body type. - Document non-200 success codes in OpenAPI with
responses={}.
Intermediate Examples
8. Dependency Injection with Depends
Share sessions and settings across routes.
from collections.abc import Generator
from fastapi import Depends, FastAPI
app = FastAPI()
def get_db() -> Generator[dict[str, str], None, None]:
db = {"connected": "true"}
yield db
@app.get("/items")
def list_items(db: dict[str, str] = Depends(get_db)) -> dict[str, str]:
return dbDependsresolves providers once per request.- Use
yieldfor setup and teardown of connections. - Override dependencies in tests via
app.dependency_overrides.
Related: Dependency Injection - sub-dependencies and scopes
9. APIRouter Modules
Split large APIs into feature routers.
from fastapi import APIRouter, FastAPI
users_router = APIRouter(prefix="/users", tags=["users"])
@users_router.get("/")
def list_users() -> list[str]:
return ["ada", "grace"]
app = FastAPI()
app.include_router(users_router)APIRoutergroups endpoints with shared prefix and tags.- One router per domain keeps files maintainable.
- Mount routers from feature packages for clean imports.
Related: Deploying FastAPI - production project layout
10. Async Route Handler
Non-blocking I/O with async def endpoints.
import httpx
from fastapi import FastAPI
app = FastAPI()
@app.get("/external")
async def proxy() -> dict[str, str]:
async with httpx.AsyncClient() as client:
resp = await client.get("https://httpbin.org/get", timeout=5.0)
return {"status": str(resp.status_code)}- Use
async defwhen awaiting HTTP, database, or cache I/O. - Do not call blocking libraries inside async routes without
run_in_threadpool. - Pair async routes with async database drivers like asyncpg.
Related: Async Routes & Databases - async SQLAlchemy sessions
Stack versions: This page was written for Python 3.14.0 (stable 3.14, maintenance 3.13), FastAPI 0.115+, Django 5.2, Flask 3.1, Pydantic 2, PyTorch 2.6+, pandas 2.2+, Polars 1.x, ruff 0.9+, and uv 0.6+.