Overview of FastAPI
FastAPI is a modern Python web framework created by Sebastián Ramírez, designed to build high-performance APIs with minimal code. Its main goals are rapid development, high performance, automatic documentation generation, and strict type checking.
1. Key Features of FastAPI
1.1 High Performance
FastAPI is built on Python’s asynchronous capabilities (asyncio) and uses Starlette as the web core. This allows the framework to achieve performance comparable to Node.js and Go. Key points:
- Supports both asynchronous (
async def) and synchronous (def) endpoints. - Can scale via Uvicorn, Gunicorn, or any ASGI server.
- Compatible with asynchronous clients and databases.
1.2 Typing and Pydantic
FastAPI uses Pydantic for data validation and serialization:
- Validates input parameters (
query,path,body) on the fly. - Automatically generates OpenAPI and JSON Schema definitions.
- Supports type conversions (e.g.,
str → int,str → datetime).
Example:
from fastapi import FastAPI, Query
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
price: float
is_offer: bool | None = None
@app.post("/items/")
async def create_item(item: Item):
return {"item_name": item.name, "item_price": item.price}
FastAPI automatically ensures that price is a number and name is a string, returning detailed errors if validation fails.
1.3 Automatic Documentation
FastAPI automatically generates documentation:
- Swagger UI:
/docs - Redoc:
/redoc - OpenAPI JSON spec:
/openapi.json
This accelerates integration with frontends or external services.
1.4 Dependency Injection (DI)
FastAPI provides a powerful dependency injection system for easy decomposition of application logic:
from fastapi import Depends
def get_db():
db = "Database connection"
try:
yield db
finally:
print("Closing DB connection")
@app.get("/users/")
async def read_users(db: str = Depends(get_db)):
return {"db": db}
DI Advantages:
- Resource lifecycle management
- Dependency reuse
- Easy testing with mocks
2. Internal Architecture of FastAPI
FastAPI is built on two key components: Starlette and Pydantic.
2.1 Starlette — Asynchronous Core
Starlette handles:
- Routing
- Middleware and request handling
- WebSocket support
- Background tasks
- TestClient for integration tests
- Asynchronous events (startup/shutdown)
FastAPI extends Starlette by:
- Adding Pydantic model support
- Generating OpenAPI schemas
- Handling documentation and validation
2.2 Pydantic — Strict Data Validation
Pydantic is used for:
- JSON validation and serialization
- Type checking and automatic conversion
- Generating OpenAPI schemas
- Handling nested models
Example with nested models:
class Owner(BaseModel):
name: str
age: int
class Car(BaseModel):
model: str
owner: Owner
car = Car(model="Tesla", owner={"name": "John", "age": "30"})
print(car.owner.age) # → 30, automatically converted to int
3. Main Components of FastAPI
3.1 Routing
FastAPI supports:
- HTTP methods: GET, POST, PUT, DELETE, PATCH, OPTIONS
- Parameters:
path,query,body,header,cookie - Routes with typed parameters
Example with parameters:
@app.get("/items/{item_id}")
async def read_item(item_id: int, q: str | None = None):
return {"item_id": item_id, "q": q}
3.2 Middleware
Middleware allows global request/response processing:
from starlette.middleware.base import BaseHTTPMiddleware
class SimpleMiddleware(BaseHTTPMiddleware):
async def dispatch(self, request, call_next):
print("Before request")
response = await call_next(request)
print("After request")
return response
app.add_middleware(SimpleMiddleware)
3.3 Background Tasks
FastAPI supports background tasks after responding to the client:
from fastapi import BackgroundTasks
def write_log(message: str):
with open("log.txt", "a") as f:
f.write(message + "\n")
@app.post("/send/")
async def send_email(background_tasks: BackgroundTasks):
background_tasks.add_task(write_log, "Email sent")
return {"message": "Request received"}
3.4 Event Handlers
Previously, FastAPI supported application events: startup and shutdown:
@app.on_event("startup")
async def startup_event():
print("App started")
@app.on_event("shutdown")
async def shutdown_event():
print("App shutdown")
Now, in modern versions of FastAPI, it is preferable to use lifespan instead of the old @app.on_event("startup") / @app.on_event("shutdown"). Lifespan allows you to combine startup and shutdown logic in a single asynchronous context manager.
from fastapi import FastAPI
from contextlib import asynccontextmanager
@asynccontextmanager
async def lifespan(app: FastAPI):
# Initialization code at application startup
print("App startup — preparing resources")
yield # The application handles requests here
# Shutdown code at application stop
print("App shutdown — releasing resources")
app = FastAPI(lifespan=lifespan)
@app.get("/")
async def read_root():
return {"message": "Hello, FastAPI!"}
Explanations:
- Everything before
yieldis executed once at application startup. - Everything after
yieldis executed at application shutdown. - The old decorators
@app.on_event("startup")and@app.on_event("shutdown")are ignored iflifespanis defined.
This approach is recommended for all new FastAPI projects.
3.5 WebSocket Support
from fastapi import WebSocket
@app.websocket("/ws")
async def websocket_endpoint(ws: WebSocket):
await ws.accept()
while True:
data = await ws.receive_text()
await ws.send_text(f"Message received: {data}")
3.6 Security and OAuth2
FastAPI integrates with OAuth2 and JWT:
from fastapi.security import OAuth2PasswordBearer
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
@app.get("/users/me")
async def read_users_me(token: str = Depends(oauth2_scheme)):
return {"token": token}
4. Asynchronous Operations and Performance
FastAPI fully supports async/await:
- Asynchronous routes are non-blocking
- Ideal for async databases (
asyncpg, Tortoise ORM) or HTTP clients (httpx) - Compatible with multithreaded or multiprocess servers
Async HTTP example:
import httpx
@app.get("/proxy")
async def proxy():
async with httpx.AsyncClient() as client:
r = await client.get("https://example.com")
return {"status": r.status_code, "content": r.text[:50]}
5. Testing FastAPI Applications
FastAPI works seamlessly with pytest and provides TestClient for synchronous testing:
from fastapi.testclient import TestClient
client = TestClient(app)
def test_read_item():
response = client.get("/items/42")
assert response.status_code == 200
assert response.json() == {"item_id": 42, "q": None}
6. Additional Features
- GZip Middleware for response compression
- Dependency overrides for testing or service replacement
7. Database Integration
FastAPI does not enforce any ORM:
- SQLAlchemy (sync & async)
- Tortoise ORM (async)
- MongoDB via
motor(async) - Redis via
aioredis
Async SQLAlchemy example:
from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine
from sqlalchemy.orm import sessionmaker
DATABASE_URL = "sqlite+aiosqlite:///./test.db"
engine = create_async_engine(DATABASE_URL, echo=True)
async_session = sessionmaker(engine, class_=AsyncSession, expire_on_commit=False)
async def get_db():
async with async_session() as session:
yield session
8. Advanced Features and Internal Architecture
8.1 Request Flow
HTTP Request → ASGI server (Uvicorn) → Starlette → FastAPI router → DI → Endpoint → Response → Client
- Starlette converts HTTP requests to ASGI events
- FastAPI handles DI and Pydantic validation
- Endpoint returns a response
8.2 Global Dependencies
app = FastAPI(dependencies=[Depends(verify_token)])
All endpoints automatically go through authentication.
8.3 Exception Handlers
FastAPI allows you to create custom error handlers for individual exceptions or globally:
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse
app = FastAPI()
@app.exception_handler(ValueError)
async def value_error_handler(request: Request, exc: ValueError):
return JSONResponse(
status_code=400,
content={"error": "Invalid value", "details": str(exc)},
)
Centralizes error handling and returns clear API responses.
8.4 Response Models
class UserOut(BaseModel):
username: str
email: str
@app.get("/users/{user_id}", response_model=UserOut)
async def read_user(user_id: int):
return {"username": "fastapi", "email": "fastapi@example.com"}
8.5 Streaming Responses
Supports StreamingResponse for large files or server-sent events:
from fastapi import FastAPI
from fastapi.responses import StreamingResponse
import asyncio
app = FastAPI()
async def iterfile():
for i in range(10):
yield f"line {i}\n"
await asyncio.sleep(0.5)
@app.get("/stream")
async def stream():
return StreamingResponse(iterfile(), media_type="text/plain")
Used for streaming logs, large files or SSE (Server-Sent Events).
8.6 File Upload and Form Data
from fastapi import FastAPI, File, UploadFile
app = FastAPI()
@app.post("/upload/")
async def upload(file: UploadFile = File(...)):
content = await file.read()
return {"filename": file.filename, "size": len(content)}
- Efficient for large files
- Does not load everything into memory at once
8.7 CORS Middleware
FastAPI allows you to configure CORS via built-in middleware to allow cross-domain requests:
from fastapi.middleware.cors import CORSMiddleware
app.add_middleware(
CORSMiddleware,
allow_origins=["https://example.com"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
Used for frontend-backend communication across different domains.
8.8 Sub-Applications and Mounts
FastAPI allows you to mount sub-applications within the main application. This is convenient for admin panels, API versioning, or integrating third-party services.
from fastapi import FastAPI
app = FastAPI()
admin_app = FastAPI()
@admin_app.get("/dashboard")
async def dashboard():
return {"admin": True}
app.mount("/admin", admin_app) # admin_app routes accessible at /admin
Features:
- A sub-application can have its own middleware and dependencies.
- The main application continues to operate independently.
- Used for code isolation and API versioning.
9. Background Tasks and Message Brokers
FastAPI integrates easily with Celery, Redis Queue, or Dramatiq:
@celery.task
def send_email(email):
print(f"Sent to {email}")
10. FastAPI + GraphQL
graphql_app = GraphQLRouter(schema)
app.include_router(graphql_app, prefix="/graphql")
11. Asynchronous Serialization and Custom Responses
@app.get("/json", response_class=ORJSONResponse)
@app.get("/html", response_class=HTMLResponse)
Conclusion
FastAPI is more than just a REST API framework. It is a platform for building complex asynchronous systems:
- Handles millions of requests with minimal latency
- Integrates seamlessly with microservices and external APIs
- Supports type-safe, readable, and declarative code
- Provides automatic documentation, testability, and extensibility
FastAPI combines modern Python features with high-performance design, making it ideal for both simple APIs and large-scale, production-grade systems.