DEV Community

ZNY
ZNY

Posted on

The Complete Guide to Building REST APIs with FastAPI in 2026

The Complete Guide to Building REST APIs with FastAPI in 2026

FastAPI became the dominant Python web framework in 2025-2026, combining async performance with automatic OpenAPI documentation and Pydantic validation. For Python backends, it's the clear choice for new projects.

Here's the practical guide.

Your First FastAPI App

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI(title="My API", version="1.0.0")

class User(BaseModel):
    id: int
    name: str
    email: str
    active: bool = True

# In-memory DB
users_db: dict[int, User] = {}

@app.get("/")
def read_root():
    return {"message": "Hello, FastAPI!"}

@app.get("/users/{user_id}")
def get_user(user_id: int):
    if user_id not in users_db:
        raise HTTPException(status_code=404, detail="User not found")
    return users_db[user_id]

@app.post("/users/", status_code=201)
def create_user(user: User):
    users_db[user.id] = user
    return user

@app.delete("/users/{user_id}", status_code=204)
def delete_user(user_id: int):
    if user_id not in users_db:
        raise HTTPException(status_code=404, detail="User not found")
    del users_db[user_id]
Enter fullscreen mode Exit fullscreen mode
uvicorn main:app --reload  # Development
uvicorn main:app --host 0.0.0.0 --port 8000  # Production
Enter fullscreen mode Exit fullscreen mode

Path Parameters and Query

from fastapi import FastAPI, Path, Query

app = FastAPI()

@app.get("/items/{item_id}")
def get_item(
    item_id: int = Path(gt=0, description="Item ID"),
    q: str | None = Query(None, max_length=50),
    limit: int = Query(10, ge=1, le=100),
):
    return {
        "item_id": item_id,
        "query": q,
        "limit": limit,
    }
Enter fullscreen mode Exit fullscreen mode

Pydantic Models

from pydantic import BaseModel, EmailStr, Field, validator
from typing import Optional

class UserCreate(BaseModel):
    email: EmailStr
    name: str = Field(..., min_length=1, max_length=100)
    password: str = Field(..., min_length=8)

    @validator("password")
    def validate_password(cls, v):
        if not any(c.isupper() for c in v):
            raise ValueError("Must contain uppercase")
        if not any(c.isdigit() for c in v):
            raise ValueError("Must contain digit")
        return v

class UserResponse(BaseModel):
    id: int
    email: EmailStr
    name: str

    class Config:
        from_attributes = True  # Pydantic v2
Enter fullscreen mode Exit fullscreen mode

Async Database

from fastapi import FastAPI, Depends, HTTPException
from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine
from sqlalchemy.orm import sessionmaker, declarative_base
from sqlalchemy import select

DATABASE_URL = "postgresql+asyncpg://user:pass@localhost/mydb"

engine = create_async_engine(DATABASE_URL, echo=True)
async_session = sessionmaker(engine, class_=AsyncSession, expire_on_commit=False)
Base = declarative_base()

class User(Base):
    __tablename__ = "users"
    id = Column(Integer, primary_key=True, index=True)
    email = Column(String, unique=True, index=True)
    name = Column(String)

async def get_db():
    async with async_session() as session:
        yield session

@app.get("/users/{user_id}")
async def get_user(user_id: int, db: AsyncSession = Depends(get_db)):
    result = await db.execute(select(User).where(User.id == user_id))
    user = result.scalar_one_or_none()
    if not user:
        raise HTTPException(status_code=404)
    return user
Enter fullscreen mode Exit fullscreen mode

Background Tasks

from fastapi import FastAPI, BackgroundTasks
import time

def send_email(email: str, message: str):
    # Simulate email sending
    time.sleep(2)
    print(f"Sent email to {email}: {message}")

@app.post("/users/{user_id}/welcome")
async def send_welcome_email(
    user_id: int,
    background_tasks: BackgroundTasks
):
    background_tasks.add_task(send_email, f"user{user_id}@example.com", "Welcome!")
    return {"message": "Welcome email queued"}
Enter fullscreen mode Exit fullscreen mode

Dependency Injection

from fastapi import Depends, HTTPException, status
from fastapi.security import HTTPBearer
from typing import Annotated

security = HTTPBearer()

async def verify_token(token: Annotated[str, Depends(security)]):
    if token != "valid-token":
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Invalid token"
        )
    return token

@app.get("/protected")
async def protected_route(token: Annotated[str, Depends(verify_token)]):
    return {"data": "secret content"}
Enter fullscreen mode Exit fullscreen mode

OpenAPI Auto-Docs

# Docs available at:
# /docs          — Swagger UI
# /redoc         — ReDoc
# /openapi.json  — Raw OpenAPI spec
Enter fullscreen mode Exit fullscreen mode

This article contains affiliate links. If you sign up through the links above, I may earn a commission at no additional cost to you.

Ready to Build Your Online Business?

Get started with Systeme.io for free — All-in-one platform for building your online business with AI tools.

Top comments (0)