intermediate frameworks FastAPI 0.111 · Updated April 2026

FastAPI Essentials Cheatsheet

Learn FastAPI 0.111 to build high-performance Python APIs. Covers core concepts, syntax, and common patterns for developers.

· 12 min read · AI-reviewed

Quick Overview

FastAPI is a modern, high-performance web framework for building APIs with Python 3.8+ based on standard Python type hints. It’s designed for speed, ease of use, and automatic interactive API documentation (OpenAPI/Swagger UI). FastAPI excels in scenarios requiring high throughput and developer productivity, leveraging Starlette for web parts and Pydantic for data validation and serialization.

Current Stable Version: FastAPI 0.111 Install:

# Install FastAPI and Uvicorn (an ASGI server)
pip install "fastapi[standard]"

Getting Started

To go from zero to a running FastAPI application, follow these steps. You’ll need Python 3.8+ installed.

  1. Create a Project Directory & Virtual Environment

    mkdir my-fastapi-app
    cd my-fastapi-app
    python -m venv venv
    # Activate the virtual environment
    # On macOS/Linux:
    source venv/bin/activate
    # On Windows:
    venv\Scripts\activate
  2. Install FastAPI and Uvicorn FastAPI requires an ASGI server to run. Uvicorn is a popular choice. Installing "fastapi[standard]" includes Uvicorn and other common dependencies.

    pip install "fastapi[standard]"
  3. Create Your First API (main.py)

    # main.py
    from fastapi import FastAPI
    
    app = FastAPI()
    
    @app.get("/")
    async def read_root():
        """
        A simple root endpoint that returns a greeting.
        """
        return {"message": "Hello, FastAPI!"}
    
    @app.get("/items/{item_id}")
    async def read_item(item_id: int, q: str | None = None):
        """
        An endpoint demonstrating path parameters and query parameters.
        """
        if q:
            return {"item_id": item_id, "q": q}
        return {"item_id": item_id}
  4. Run the FastAPI Application From your project directory, with the virtual environment activated:

    uvicorn main:app --reload
    • main: refers to the main.py file.
    • app: refers to the app object created inside main.py.
    • --reload: automatically restarts the server on code changes (for development).
  5. Access Your API Open your browser or use a tool like curl:

    • http://127.0.0.1:8000/ will show {"message": "Hello, FastAPI!"}
    • http://127.0.0.1:8000/items/5 will show {"item_id": 5}
    • http://127.0.0.1:8000/items/5?q=somequery will show {"item_id": 5, "q": "somequery"}
  6. Check the Interactive API Docs FastAPI automatically generates OpenAPI documentation. Navigate to:

    • Swagger UI: http://127.0.0.1:8000/docs
    • ReDoc: http://127.0.0.1:8000/redoc

Core Concepts

ConceptDescription
FastAPI instanceThe main entry point for your application. Handles routing, middleware, and dependency injection.
Path OperationAn HTTP method (GET, POST, PUT, DELETE, etc.) decorator applied to an async def or def function. Defines an API endpoint.
Path ParametersVariables defined in the URL path (e.g., /items/{item_id}). Automatically extracted and type-validated.
Query ParametersOptional key-value pairs appended to the URL after ? (e.g., ?name=value). Automatically extracted and type-validated.
Request BodyData sent by the client, typically for POST, PUT, PATCH requests. Defined using Pydantic models.
Pydantic ModelsUsed for data validation, serialization, and deserialization of request bodies and response models. Leverage Python type hints.
Dependency InjectionA powerful system for managing shared logic, database sessions, authentication, etc. Functions/classes declared as dependencies are automatically resolved.
HTTPExceptionFastAPI’s built-in exception for raising HTTP errors with specific status codes and details.
APIRouterUsed to organize API endpoints into modular, reusable components, useful for larger applications.

Essential Commands / API / Syntax

1. Define Path Operations (HTTP Methods)

Use decorators to define endpoints for different HTTP methods.

from fastapi import FastAPI, HTTPException, status
from pydantic import BaseModel

app = FastAPI()

# Pydantic model for request body validation
class Item(BaseModel):
    name: str
    description: str | None = None
    price: float
    tax: float | None = None

# In-memory data store for demonstration
items_db = {}
next_item_id = 0

# GET: Retrieve data
@app.get("/items/")
async def read_items():
    """
    Retrieve all items.
    """
    return list(items_db.values())

# GET with Path Parameters
@app.get("/items/{item_id}")
async def read_single_item(item_id: int):
    """
    Retrieve a single item by ID.
    """
    if item_id not in items_db:
        raise HTTPException(status_code=404, detail="Item not found") #
    return items_db[item_id]

# GET with Query Parameters (optional, default values)
@app.get("/search/")
async def search_items(query: str | None = None, limit: int = 10):
    """
    Search for items with optional query and limit.
    """
    results = []
    if query:
        for item_id, item in items_db.items():
            if query.lower() in item.name.lower():
                results.append({"item_id": item_id, **item.model_dump()}) # Use model_dump() for Pydantic v2
    else:
        results = [{"item_id": item_id, **item.model_dump()} for item_id, item in items_db.items()]

    return results[:limit]

# POST: Create new data (with Request Body)
@app.post("/items/", status_code=status.HTTP_201_CREATED)
async def create_item(item: Item): #
    """
    Create a new item.
    """
    global next_item_id
    item_id = next_item_id
    items_db[item_id] = item
    next_item_id += 1
    return {"id": item_id, **item.model_dump()}

# PUT: Update existing data (full replacement)
@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
    """
    Update an existing item by ID.
    """
    if item_id not in items_db:
        raise HTTPException(status_code=404, detail="Item not found")
    items_db[item_id] = item
    return {"id": item_id, **item.model_dump()}

# PATCH: Partially update existing data
from typing import Optional

class ItemUpdate(BaseModel):
    name: Optional[str] = None
    description: Optional[str] = None
    price: Optional[float] = None
    tax: Optional[float] = None

@app.patch("/items/{item_id}")
async def patch_item(item_id: int, item_update: ItemUpdate):
    """
    Partially update an existing item by ID.
    """
    if item_id not in items_db:
        raise HTTPException(status_code=404, detail="Item not found")
    
    stored_item_data = items_db[item_id].model_dump()
    updated_item_data = item_update.model_dump(exclude_unset=True) # Exclude unset fields for partial update
    
    for key, value in updated_item_data.items():
        stored_item_data[key] = value

    items_db[item_id] = Item(**stored_item_data)
    return {"id": item_id, **items_db[item_id].model_dump()}


# DELETE: Remove data
@app.delete("/items/{item_id}")
async def delete_item(item_id: int):
    """
    Delete an item by ID.
    """
    if item_id not in items_db:
        raise HTTPException(status_code=404, detail="Item not found")
    del items_db[item_id]
    return {"message": "Item deleted successfully"}

2. Dependency Injection

Use Depends to inject reusable logic, authentication, database sessions, etc.

from fastapi import Depends, FastAPI, Header, HTTPException, status

app = FastAPI()

# A simple dependency function
async def verify_token(x_token: str = Header()):
    """
    A dependency to verify an X-Token header.
    """
    if x_token != "secret-token":
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="X-Token header invalid")
    return x_token

# A dependency that returns data
async def get_current_user(token: str = Depends(verify_token)):
    """
    A dependency that simulates fetching a user based on a verified token.
    """
    # In a real app, you'd decode JWT, query DB, etc.
    return {"username": "currentuser", "token": token}

@app.get("/users/me")
async def read_current_user(current_user: dict = Depends(get_current_user)):
    """
    An endpoint protected by dependencies.
    """
    return current_user

# To test:
# curl -X GET "http://127.0.0.1:8000/users/me" -H "X-Token: secret-token"

3. Organizing with APIRouter

For larger applications, use APIRouter to group related path operations.

# routers/users.py
from fastapi import APIRouter, HTTPException, status
from pydantic import BaseModel

router = APIRouter(
    prefix="/users",
    tags=["users"],
    responses={404: {"description": "User not found"}},
)

class User(BaseModel):
    username: str
    email: str | None = None
    full_name: str | None = None
    disabled: bool | None = False

users_db = {
    "foo": User(username="foo", email="[email protected]"),
    "bar": User(username="bar", email="[email protected]", disabled=True),
}

@router.get("/")
async def read_users():
    """
    Retrieve all users.
    """
    return list(users_db.values())

@router.get("/{username}")
async def read_user(username: str):
    """
    Retrieve a specific user by username.
    """
    if username not in users_db:
        raise HTTPException(status_code=404, detail="User not found")
    return users_db[username]

# main.py (updated)
# from fastapi import FastAPI
# from routers import users # Assuming 'routers' is a package with users.py

# app = FastAPI()
# app.include_router(users.router) # Include the router

Common Patterns

1. Database Integration (Basic SQLAlchemy)

This pattern demonstrates how to integrate SQLAlchemy with FastAPI, providing a session per request using a dependency.

# database.py
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker, declarative_base

# Use an in-memory SQLite database for simplicity
SQLALCHEMY_DATABASE_URL = "sqlite:///./sql_app.db"

# For PostgreSQL, use:
# SQLALCHEMY_DATABASE_URL = "postgresql://user:password@host:port/dbname"

engine = create_engine(
    SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False} # Needed for SQLite
)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)

Base = declarative_base()

# Dependency to get a database session
def get_db():
    db = SessionLocal()
    try:
        yield db
    finally:
        db.close()

# models.py
from sqlalchemy import Column, Integer, String, Boolean
# from .database import Base # Use this if models.py is in a subfolder

class Item(Base):
    __tablename__ = "items"
    id = Column(Integer, primary_key=True, index=True)
    name = Column(String, index=True)
    description = Column(String, index=True)
    is_active = Column(Boolean, default=True)

# schemas.py (Pydantic models for request/response validation)
from pydantic import BaseModel

class ItemBase(BaseModel):
    name: str
    description: str | None = None

class ItemCreate(ItemBase):
    pass

class Item(ItemBase):
    id: int
    is_active: bool

    class ConfigDict: # For Pydantic v2
        from_attributes = True # Was orm_mode = True in Pydantic v1

# crud.py (CRUD operations)
from sqlalchemy.orm import Session
# from . import models, schemas # Use this if schemas.py is in a subfolder

def get_item(db: Session, item_id: int):
    return db.query(Item).filter(Item.id == item_id).first()

def create_item(db: Session, item: ItemCreate):
    db_item = Item(name=item.name, description=item.description)
    db.add(db_item)
    db.commit()
    db.refresh(db_item)
    return db_item

# main.py (API endpoints using the database)
from fastapi import FastAPI, Depends, HTTPException
from sqlalchemy.orm import Session
# from .database import engine, Base, get_db # Use this if database.py is in a subfolder
# from . import models, schemas, crud # Use this if schemas.py, crud.py are in subfolders

# Create tables
# models.Base.metadata.create_all(bind=engine) # Uncomment and run once

app = FastAPI()

@app.post("/sql-items/", response_model=Item, status_code=status.HTTP_201_CREATED)
def create_sql_item(item: ItemCreate, db: Session = Depends(get_db)):
    """
    Create an item in the database.
    """
    return crud.create_item(db=db, item=item)

@app.get("/sql-items/{item_id}", response_model=Item)
def read_sql_item(item_id: int, db: Session = Depends(get_db)):
    """
    Retrieve an item from the database.
    """
    db_item = crud.get_item(db, item_id=item_id)
    if db_item is None:
        raise HTTPException(status_code=404, detail="Item not found")
    return db_item

2. OAuth2 with Password Bearer Token

This shows a basic structure for implementing OAuth2 Password Flow for authentication using FastAPI’s security utilities.

from fastapi import Depends, FastAPI, HTTPException, status
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from pydantic import BaseModel

app = FastAPI()

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token") #

class UserInDB(BaseModel):
    username: str
    hashed_password: str
    full_name: str | None = None
    disabled: bool | None = None

class Token(BaseModel):
    access_token: str
    token_type: str

class TokenData(BaseModel):
    username: str | None = None

# Dummy user database
fake_users_db = {
    "john": {
        "username": "john",
        "hashed_password": "fakehashedsecret", # In production, hash passwords securely!
        "full_name": "John Doe",
        "disabled": False,
    },
    "jane": {
        "username": "jane",
        "hashed_password": "fakehashedpassword",
        "full_name": "Jane Doe",
        "disabled": True,
    },
}

def get_user(username: str):
    if username in fake_users_db:
        user_dict = fake_users_db[username]
        return UserInDB(**user_dict)

async def authenticate_user(username: str, password: str):
    user = get_user(username)
    if not user:
        return False
    if password != user.hashed_password.replace("fakehashed", ""): # Simulating password check
        return False
    return user

async def get_current_user_oauth(token: str = Depends(oauth2_scheme)):
    user = get_user(token) # Here, token is just the username for this simple example
    if not user:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Invalid authentication credentials",
            headers={"WWW-Authenticate": "Bearer"},
        )
    if user.disabled:
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Inactive user")
    return user

@app.post("/token", response_model=Token)
async def login_for_access_token(form_data: OAuth2PasswordRequestForm = Depends()):
    """
    Endpoint for clients to get an access token.
    """
    user = await authenticate_user(form_data.username, form_data.password)
    if not user:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Incorrect username or password",
            headers={"WWW-Authenticate": "Bearer"},
        )
    access_token = user.username # In real app, generate JWT token
    return {"access_token": access_token, "token_type": "bearer"} #

@app.get("/oauth_users/me/")
async def read_users_me(current_user: UserInDB = Depends(get_current_user_oauth)):
    """
    Protected endpoint to get current user details.
    """
    return current_user

# To test:
# 1. Get token: curl -X POST -d "username=john&password=secret" http://127.0.0.1:8000/token
# 2. Use token: curl -X GET "http://127.0.0.1:8000/oauth_users/me/" -H "Authorization: Bearer john"

Gotchas & Tips

  • Asynchronous Code (async def): FastAPI is built on ASGI and uses async/await for asynchronous operations. Use async def for path operations that perform I/O-bound tasks (e.g., database queries, external API calls) to allow the server to handle other requests concurrently. If your function is CPU-bound, def is generally fine, and FastAPI will run it in a separate thread pool.
  • CORS (Cross-Origin Resource Sharing): If your frontend is served from a different origin (domain, port, or protocol) than your FastAPI backend, you’ll encounter CORS issues. Use CORSMiddleware to configure allowed origins.
    from fastapi import FastAPI
    from fastapi.middleware.cors import CORSMiddleware
    
    app = FastAPI()
    
    origins = [
        "http://localhost:3000", # React app, Vue app, etc.
        "https://yourfrontend.com",
    ]
    
    app.add_middleware(
        CORSMiddleware,
        allow_origins=origins, # List of allowed origins
        allow_credentials=True,
        allow_methods=["*"], # Allow all methods (GET, POST, etc.)
        allow_headers=["*"], # Allow all headers
    )
    
    @app.get("/")
    async def main():
        return {"message": "Hello CORS!"}
    Always add CORSMiddleware as one of the first middlewares.
  • Background Tasks: For operations that don’t need to block the HTTP response (e.g., sending emails, writing logs), use BackgroundTasks.
    from fastapi import FastAPI, BackgroundTasks, Depends
    
    app = FastAPI()
    
    def write_log(message: str):
        with open("log.txt", mode="a") as log:
            log.write(message + "\n")
    
    @app.post("/send-notification/")
    async def send_notification(
        email: str,
        message: str,
        background_tasks: BackgroundTasks
    ):
        """
        Send a notification and log it in the background.
        """
        background_tasks.add_task(write_log, f"Notification sent to {email}: {message}")
        return {"message": "Notification sent in background!"}
  • Testing with TestClient: FastAPI provides TestClient for synchronous testing of your application without a running server, using the httpx library.
    from fastapi.testclient import TestClient
    # from main import app # Assuming your main app is in main.py
    
    client = TestClient(app)
    
    def test_read_root():
        response = client.get("/")
        assert response.status_code == 200
        assert response.json() == {"message": "Hello, FastAPI!"}
    
    def test_read_item():
        response = client.get("/items/123?q=test")
        assert response.status_code == 200
        assert response.json() == {"item_id": 123, "q": "test"}
    
    def test_create_item():
        response = client.post(
            "/items/",
            json={"name": "New Item", "price": 10.99, "description": "A test item"}
        )
        assert response.status_code == 201
        assert response.json()["name"] == "New Item"
  • Version-specific model_dump() for Pydantic v2: As of Pydantic v2 (which FastAPI 0.111 typically uses), obj.dict() for serializing a Pydantic model becomes obj.model_dump(). For orm_mode = True in Config, use model_config = ConfigDict(from_attributes=True).

Next Steps

  • Official FastAPI Documentation: The official docs are exceptionally well-written and comprehensive. Start with the “Tutorial - User Guide” for deeper dives into every feature. https://fastapi.tiangolo.com/
  • Pydantic Documentation: Since FastAPI heavily relies on Pydantic for data validation and settings, understanding Pydantic is crucial. https://docs.pydantic.dev/
  • Starlette Documentation: FastAPI builds on Starlette. While you typically interact with FastAPI’s abstractions, understanding Starlette can be useful for advanced middleware or custom responses. https://www.starlette.io/
  • Alembic: For managing database migrations with SQLAlchemy, Alembic is the standard tool. https://alembic.sqlalchemy.org/

Source: z2h.fyi/cheatsheets/fastapi — Zero to Hero cheatsheets for developers.

Source: z2h.fyi/cheatsheets/fastapi-cheatsheet — Zero to Hero cheatsheets for developers.