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
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
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)
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()
Follow me for more Python tips! 🐍
💡 Related: **Content Creator Ultimate Bundle (Save 33%)* — $29.99*
喜欢这篇文章?关注获取更多Python自动化内容!
Top comments (0)