FastAPI is a Python web framework for building APIs. It's built on top of Starlette (ASGI web toolkit) and Pydantic (data validation via Python type hints).
Key things it gives you out of the box:
You describe your data shapes once as Python classes, and FastAPI handles parsing, validation, serialization, and documentation from that single source of truth.
FastAPI itself doesn't run a server or handle raw sockets — it sits on top of a small stack, and each layer has one job:
An ASGI server. It accepts raw HTTP/WebSocket connections and turns them into Python calls. ASGI (Asynchronous Server Gateway Interface) is the async successor to WSGI — it's what makes native async def endpoints possible.
The underlying web toolkit FastAPI is built on. It provides routing, middleware, background tasks, and the request/response objects. FastAPI adds validation and docs generation on top of it.
Handles data validation and serialization. Your type hints (item_id: int, a BaseModel subclass) aren't just documentation — Pydantic actually parses and validates against them at runtime.
Request flow:
Create an isolated environment, then install FastAPI and an ASGI server (Uvicorn):
Minimal recommended project layout for a single-file app:
uvicorn[standard] pulls in extras like uvloop and httptools for better performance — plain uvicorn also works for learning/dev.app is the core application object. Every route is registered on it using a decorator that matches the HTTP method: @app.get, @app.post, @app.put, @app.delete, etc.
Returning a Python dict is enough — FastAPI serializes it to JSON automatically.
main:app means "in main.py, use the object named app". --reload restarts the server on code changes — dev only, not for production.
Once running, FastAPI gives you free interactive docs:
def is fine and when you need async def.FastAPI lets you write either def or async def for the same endpoint, and both work — but they behave differently under load.
A regular def endpoint runs in a worker thread pool, so it won't block the main event loop even if it does blocking work. An async def endpoint runs directly on the event loop — which is efficient only if everything it awaits is also async.
The trap: calling a blocking library (e.g. a sync DB driver, requests) inside an async def function blocks the entire event loop — every other request on that worker stalls too.
async def isn't automatically faster — it's only faster when every I/O call inside it is also non-blocking.The type hint (item_id: int) is enforced automatically. /items/abc returns a 422 Unprocessable Entity with a clear validation error — no manual parsing or checks needed.
Call it as /items/?skip=0&limit=5&q=phone. Parameters with a default value are optional; those without one are required.
A BaseModel subclass declares the expected JSON shape. FastAPI parses the request body into an Item instance, validates every field, and rejects malformed requests before your function even runs.
response_model filters and shapes the output — useful for hiding internal-only fields (like a stored ID or a hashed password) even if the object you return has more attributes than the model declares.
response_model — they can be different classes.A dependency is just a function FastAPI calls for you before your endpoint runs, and whose return value it passes in as an argument. It's how FastAPI avoids repeating the same setup code (DB session, current user, pagination defaults) in every route.
Depends(get_db) tells FastAPI: "run get_db, and give me whatever it yields." The same dependency can be reused across dozens of routes, and it's swappable in tests — you override get_db with a fake one instead of touching a real database.
A dependency can itself depend on other dependencies, which is how auth is usually layered in:
Raise HTTPException to stop execution and return a specific status code with a JSON error body:
Common status codes you'll actually use:
For errors that aren't tied to one route, register a global handler instead of repeating try/except everywhere:
WSGI (sync by default). Minimal core, huge ecosystem of extensions. No built-in validation or docs — you add libraries (Marshmallow, Flask-RESTX) for that yourself.
Batteries-included: ORM, admin panel, auth, migrations all ship together. Traditionally sync/WSGI; Django REST Framework is the standard way to build APIs with it. Best fit for large, full-stack apps that want one opinionated way to do things.
ASGI (async-native). No ORM or admin panel bundled — it's an API layer, not a full framework. Its edge is type-hint-driven validation and docs generation, and first-class async support for high-concurrency I/O-bound services.
To run the example: