FastAPI is the fastest-growing Python web framework — 78K+ GitHub stars, async by default, automatic OpenAPI docs, and type-safe validation. All free and open source.
Why FastAPI?
- Fastest Python framework — on par with Node.js and Go
- Automatic API docs — Swagger UI and ReDoc generated from code
- Type validation — Pydantic v2 validates all inputs automatically
- Async native — built on ASGI, supports async/await
- Dependency injection �� clean, testable code
- Production-ready — used by Microsoft, Netflix, Uber
Quick Start
pip install fastapi uvicorn
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
async def root():
return {"message": "Hello, FastAPI!"}
@app.get("/users/{user_id}")
async def get_user(user_id: int):
return {"user_id": user_id, "name": "Alice"}
# Run with: uvicorn main:app --reload
# Docs at: http://localhost:8000/docs
Pydantic Models (Auto-Validation)
from pydantic import BaseModel, EmailStr, Field
from datetime import datetime
class CreateUser(BaseModel):
name: str = Field(min_length=2, max_length=50)
email: EmailStr
age: int = Field(ge=18, le=120)
role: str = Field(default="user", pattern="^(admin|user|moderator)$")
class UserResponse(BaseModel):
id: int
name: str
email: str
created_at: datetime
@app.post("/users", response_model=UserResponse, status_code=201)
async def create_user(user: CreateUser):
# user is already validated — invalid data returns 422 automatically
db_user = await db.create_user(**user.model_dump())
return db_user
Send invalid data? FastAPI returns detailed error:
{
"detail": [
{
"loc": ["body", "email"],
"msg": "value is not a valid email address",
"type": "value_error.email"
}
]
}
Query Parameters
from typing import Optional
from enum import Enum
class SortOrder(str, Enum):
asc = "asc"
desc = "desc"
@app.get("/users")
async def list_users(
page: int = 1,
limit: int = Field(default=10, le=100),
search: Optional[str] = None,
sort: SortOrder = SortOrder.desc,
active: bool = True,
):
# All parameters are validated and typed automatically
query = db.users.filter(active=active)
if search:
query = query.filter(name__contains=search)
return await query.order_by(sort.value).offset((page-1)*limit).limit(limit).all()
Dependency Injection
from fastapi import Depends, HTTPException, Header
# Database dependency
async def get_db():
db = Database()
try:
yield db
finally:
await db.close()
# Auth dependency
async def get_current_user(authorization: str = Header()):
token = authorization.replace("Bearer ", "")
user = await verify_token(token)
if not user:
raise HTTPException(status_code=401, detail="Invalid token")
return user
# Admin dependency (chains with auth)
async def require_admin(user = Depends(get_current_user)):
if user.role != "admin":
raise HTTPException(status_code=403, detail="Admin required")
return user
@app.get("/admin/users")
async def admin_list_users(
admin = Depends(require_admin),
db = Depends(get_db),
):
return await db.users.all()
@app.get("/me")
async def my_profile(user = Depends(get_current_user)):
return user
File Upload
from fastapi import UploadFile, File
@app.post("/upload")
async def upload_file(file: UploadFile = File(...)):
contents = await file.read()
return {
"filename": file.filename,
"size": len(contents),
"content_type": file.content_type,
}
@app.post("/upload-multiple")
async def upload_multiple(files: list[UploadFile] = File(...)):
return [{"filename": f.filename, "size": f.size} for f in files]
WebSockets
from fastapi import WebSocket, WebSocketDisconnect
connected_clients: list[WebSocket] = []
@app.websocket("/ws/chat")
async def chat(websocket: WebSocket):
await websocket.accept()
connected_clients.append(websocket)
try:
while True:
message = await websocket.receive_text()
# Broadcast to all clients
for client in connected_clients:
await client.send_text(f"User: {message}")
except WebSocketDisconnect:
connected_clients.remove(websocket)
Background Tasks
from fastapi import BackgroundTasks
async def send_notification(email: str, message: str):
# This runs after the response is sent
await send_email(email, message)
await log_notification(email)
@app.post("/orders")
async def create_order(order: OrderCreate, background_tasks: BackgroundTasks):
db_order = await save_order(order)
# Schedule background work
background_tasks.add_task(send_notification, order.email, f"Order {db_order.id} confirmed!")
return db_order # Response sent immediately
Automatic OpenAPI Docs
FastAPI generates interactive API documentation at:
-
Swagger UI:
http://localhost:8000/docs -
ReDoc:
http://localhost:8000/redoc -
OpenAPI JSON:
http://localhost:8000/openapi.json
No configuration needed. Add docstrings and they show up in docs:
@app.post("/users", tags=["users"], summary="Create a new user")
async def create_user(user: CreateUser):
"""
Create a new user with the following fields:
- **name**: User's display name (2-50 chars)
- **email**: Valid email address
- **age**: Must be 18+
"""
...
FastAPI vs Django vs Flask vs Express
| Feature | FastAPI | Django | Flask | Express |
|---|---|---|---|---|
| Speed | Fastest (Python) | Medium | Medium | Fast |
| Async | Native | Optional | No | Native |
| Validation | Automatic (Pydantic) | Forms/DRF | Manual | Manual |
| API docs | Automatic | DRF plugin | Plugin | Plugin |
| ORM | Any (SQLAlchemy) | Built-in | Any | Any |
| Auth | Dependency injection | Built-in | Plugin | Plugin |
| Learning curve | Low | High | Low | Low |
Need to scrape data from any website and get it in structured JSON? Check out my web scraping tools on Apify — no coding required, results in minutes.
Have a custom data extraction project? Email me at spinov001@gmail.com — I build tailored scraping solutions for businesses.
Top comments (0)