What is FastAPI?
FastAPI is a modern, fast (high-performance) web framework for building APIs with Python 3.7+ based on standard Python type hints. It is one of the fastest Python frameworks available, on par with NodeJS and Go. FastAPI gives you automatic interactive API documentation, data validation, serialization, and async support out of the box.
Why FastAPI?
- Fast: Very high performance thanks to Starlette and Pydantic
- Type-safe: Uses Python type hints for validation, serialization, and documentation
- Auto docs: Swagger UI and ReDoc generated automatically
- Async native: Full support for async/await
- Standards-based: Built on OpenAPI and JSON Schema
Getting Started
# Install FastAPI and Uvicorn (ASGI server)
# pip install fastapi uvicorn[standard]
# main.py - Hello World
from fastapi import FastAPI
app = FastAPI(
title="My API",
description="A demo FastAPI application",
version="1.0.0",
)
@app.get("/")
async def root():
return {"message": "Hello, World!"}
@app.get("/health")
async def health_check():
return {"status": "healthy"}
# Run with: uvicorn main:app --reload
# API docs at: http://localhost:8000/docs (Swagger)
# Alt docs at: http://localhost:8000/redoc
Path and Query Parameters
from fastapi import FastAPI, Query, Path
app = FastAPI()
# Path parameters
@app.get("/users/{user_id}")
async def get_user(user_id: int):
return {"user_id": user_id}
# Path parameter validation
@app.get("/items/{item_id}")
async def get_item(
item_id: int = Path(..., title="Item ID", ge=1, le=1000)
):
return {"item_id": item_id}
# Query parameters
@app.get("/items")
async def list_items(
skip: int = Query(default=0, ge=0),
limit: int = Query(default=10, ge=1, le=100),
search: str | None = Query(default=None, min_length=1, max_length=50),
):
return {
"skip": skip,
"limit": limit,
"search": search,
}
# GET /items?skip=0&limit=20&search=laptop
# Enum path parameters
from enum import Enum
class ModelName(str, Enum):
alexnet = "alexnet"
resnet = "resnet"
lenet = "lenet"
@app.get("/models/{model_name}")
async def get_model(model_name: ModelName):
return {"model": model_name, "message": f"Using {model_name.value}"}
Request Body with Pydantic
from pydantic import BaseModel, EmailStr, Field
from datetime import datetime
# Define request/response models
class UserCreate(BaseModel):
name: str = Field(..., min_length=1, max_length=100)
email: EmailStr
age: int = Field(..., ge=0, le=150)
bio: str | None = None
class UserResponse(BaseModel):
id: int
name: str
email: str
age: int
bio: str | None
created_at: datetime
model_config = {"from_attributes": True}
# Use models in endpoints
@app.post("/users", response_model=UserResponse, status_code=201)
async def create_user(user: UserCreate):
# FastAPI automatically:
# 1. Parses the JSON body
# 2. Validates against the Pydantic model
# 3. Returns 422 with details if validation fails
new_user = {
"id": 1,
"name": user.name,
"email": user.email,
"age": user.age,
"bio": user.bio,
"created_at": datetime.now(),
}
return new_user
# Nested models
class Address(BaseModel):
street: str
city: str
country: str
zip_code: str
class UserWithAddress(BaseModel):
name: str
email: EmailStr
address: Address
tags: list[str] = []
@app.post("/users/full")
async def create_full_user(user: UserWithAddress):
return user
Dependency Injection
from fastapi import FastAPI, Depends, HTTPException, Header
app = FastAPI()
# Simple dependency
async def get_db():
db = {"connection": "active"} # Simulated DB connection
try:
yield db
finally:
pass # Close connection here
# Auth dependency
async def verify_token(authorization: str = Header(...)):
if not authorization.startswith("Bearer "):
raise HTTPException(status_code=401, detail="Invalid token format")
token = authorization.replace("Bearer ", "")
if token != "secret-token":
raise HTTPException(status_code=401, detail="Invalid token")
return {"user_id": 1, "role": "admin"}
# Use dependencies in endpoints
@app.get("/protected")
async def protected_route(
user: dict = Depends(verify_token),
db: dict = Depends(get_db),
):
return {"message": f"Hello user {user['user_id']}", "db": db}
# Reusable dependency classes
class Pagination:
def __init__(self, skip: int = 0, limit: int = 10):
self.skip = skip
self.limit = limit
@app.get("/posts")
async def list_posts(pagination: Pagination = Depends()):
return {"skip": pagination.skip, "limit": pagination.limit}
Key Takeaways
- Type hints drive everything: Validation, docs, and serialization from types
- Pydantic models: Define your API contracts as Python classes
- Dependency injection: Clean, reusable, and testable dependencies
- Auto documentation: Swagger UI at /docs, ReDoc at /redoc