TechLead
Lesson 14 of 25
5 min read
Python

FastAPI Fundamentals

Build modern, high-performance APIs with FastAPI, automatic documentation, and async support

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

Continue Learning