DEV Community

qing
qing

Posted on

5x Faster APIs

FastAPI + Pydantic v2: Building Production REST APIs

FastAPI with Pydantic v2 is 5-50x faster than v1. Here's how to build production-ready APIs.

Install

pip install fastapi uvicorn pydantic>=2.0
Enter fullscreen mode Exit fullscreen mode

Pydantic v2 Models

from pydantic import BaseModel, Field, field_validator, model_validator
from typing import Optional
from datetime import datetime

class UserCreate(BaseModel):
    username: str = Field(min_length=3, max_length=50, pattern=r'^[a-zA-Z0-9_]+$')
    email: str = Field(pattern=r'^[^@]+@[^@]+\.[^@]+$')
    age: int = Field(ge=0, le=150)
    bio: Optional[str] = Field(default=None, max_length=500)

    @field_validator('username')
    @classmethod
    def username_must_not_be_reserved(cls, v: str) -> str:
        reserved = {'admin', 'root', 'api', 'null', 'undefined'}
        if v.lower() in reserved:
            raise ValueError(f"'{v}' is a reserved username")
        return v.lower()

class UserResponse(BaseModel):
    id: int
    username: str
    email: str
    created_at: datetime

    model_config = {"from_attributes": True}

class PaginatedResponse(BaseModel):
    items: list[UserResponse]
    total: int
    page: int
    page_size: int
    has_next: bool
Enter fullscreen mode Exit fullscreen mode

Complete FastAPI Application

from fastapi import FastAPI, HTTPException, Depends, Query
from fastapi.middleware.cors import CORSMiddleware
from typing import Annotated
import uvicorn

app = FastAPI(
    title="My API",
    version="1.0.0",
    description="Production-ready API with FastAPI + Pydantic v2",
)

app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_methods=["*"],
    allow_headers=["*"],
)

# Simulated database
users_db: dict[int, dict] = {}
next_id = 1

@app.post("/users/", response_model=UserResponse, status_code=201)
async def create_user(user: UserCreate):
    global next_id
    if any(u["email"] == user.email for u in users_db.values()):
        raise HTTPException(status_code=409, detail="Email already registered")
    new_user = {
        "id": next_id,
        "username": user.username,
        "email": user.email,
        "created_at": datetime.utcnow(),
    }
    users_db[next_id] = new_user
    next_id += 1
    return new_user

@app.get("/users/", response_model=PaginatedResponse)
async def list_users(
    page: Annotated[int, Query(ge=1)] = 1,
    page_size: Annotated[int, Query(ge=1, le=100)] = 20,
):
    all_users = list(users_db.values())
    start = (page - 1) * page_size
    items = all_users[start:start + page_size]
    return {
        "items": items,
        "total": len(all_users),
        "page": page,
        "page_size": page_size,
        "has_next": start + page_size < len(all_users),
    }

@app.get("/users/{user_id}", response_model=UserResponse)
async def get_user(user_id: int):
    user = users_db.get(user_id)
    if not user:
        raise HTTPException(status_code=404, detail=f"User {user_id} not found")
    return user

@app.delete("/users/{user_id}", status_code=204)
async def delete_user(user_id: int):
    if user_id not in users_db:
        raise HTTPException(status_code=404, detail=f"User {user_id} not found")
    del users_db[user_id]

if __name__ == "__main__":
    uvicorn.run(app, host="0.0.0.0", port=8000, reload=True)
Enter fullscreen mode Exit fullscreen mode

Settings with Pydantic v2

from pydantic_settings import BaseSettings

class Settings(BaseSettings):
    app_name: str = "My API"
    debug: bool = False
    database_url: str = "sqlite:///./app.db"
    secret_key: str = "change-this-in-production"

    model_config = {"env_file": ".env"}

settings = Settings()
Enter fullscreen mode Exit fullscreen mode

Follow me for more Python tips! 🐍


💡 Related: **Content Creator Ultimate Bundle (Save 33%)* — $29.99*


喜欢这篇文章?关注获取更多Python自动化内容!

Top comments (0)