We earn commissions when you shop through the links below.
If you want to know how to build a Python API with FastAPI, you’ve picked the right framework. FastAPI has become my go-to for any new Python backend project — it’s fast, it’s typed, and it generates interactive docs automatically. Whether you’re building a microservice, a SaaS backend, or just exposing some data over HTTP, FastAPI gets out of your way and lets you ship quickly.
In this guide I’ll walk you through everything from setup to a working CRUD API with input validation, a database layer, and deployment. Let’s get into it.
Why FastAPI?
Before we dive into code, a quick pitch. FastAPI is built on top of Starlette (ASGI) and Pydantic. This means:
- Async support out of the box
- Automatic request validation via Pydantic models
- Auto-generated OpenAPI (Swagger) docs at
/docs - Python type hints drive everything — your editor and the framework are always in sync
I’ve used Flask and Django REST Framework heavily in the past. FastAPI consistently outperforms both for API-first projects in terms of developer experience and raw throughput.
Project Setup
I’m assuming you have Python 3.11+ installed. Start by creating a virtual environment and installing dependencies:
python -m venv venv
source venv/bin/activate # Windows: venv\Scripts\activate
pip install fastapi uvicorn sqlalchemy pydanticCreate a project structure like this:
myapi/
app/
__init__.py
main.py
models.py
schemas.py
database.py
routers/
__init__.py
items.py
requirements.txtYour First Endpoint
Let’s start simple. Here’s app/main.py:
from fastapi import FastAPI
app = FastAPI(title="My API", version="1.0.0")
@app.get("/")
def root():
return {"message": "Hello, World!"}
@app.get("/health")
def health_check():
return {"status": "ok"}Run it:
uvicorn app.main:app --reloadOpen http://localhost:8000/docs and you’ll see the Swagger UI already populated. That’s FastAPI doing its thing — no extra setup needed.
Request Validation with Pydantic
One of the best parts of learning how to build a Python API with FastAPI is discovering how Pydantic handles validation. You define a schema, and FastAPI does the rest — parsing, validating, and returning a clear 422 error if the request doesn’t match.
Create app/schemas.py:
from pydantic import BaseModel, Field
from typing import Optional
class ItemBase(BaseModel):
name: str = Field(..., min_length=1, max_length=100)
description: Optional[str] = None
price: float = Field(..., gt=0)
in_stock: bool = True
class ItemCreate(ItemBase):
pass
class ItemResponse(ItemBase):
id: int
class Config:
from_attributes = TrueDatabase Integration with SQLAlchemy
For persistence, I’ll use SQLAlchemy with SQLite for simplicity (swap the connection string for Postgres in production).
app/database.py:
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
DATABASE_URL = "sqlite:///./items.db"
engine = create_engine(DATABASE_URL, connect_args={"check_same_thread": False})
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
Base = declarative_base()
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()app/models.py:
from sqlalchemy import Column, Integer, String, Float, Boolean
from .database import Base
class Item(Base):
__tablename__ = "items"
id = Column(Integer, primary_key=True, index=True)
name = Column(String, index=True)
description = Column(String, nullable=True)
price = Column(Float)
in_stock = Column(Boolean, default=True)Building the CRUD Router
Now the real meat. app/routers/items.py:
from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy.orm import Session
from typing import List
from ..database import get_db
from ..models import Item
from ..schemas import ItemCreate, ItemResponse
router = APIRouter(prefix="/items", tags=["Items"])
@router.get("/", response_model=List[ItemResponse])
def list_items(skip: int = 0, limit: int = 20, db: Session = Depends(get_db)):
return db.query(Item).offset(skip).limit(limit).all()
@router.get("/{item_id}", response_model=ItemResponse)
def get_item(item_id: int, db: Session = Depends(get_db)):
item = db.query(Item).filter(Item.id == item_id).first()
if not item:
raise HTTPException(status_code=404, detail="Item not found")
return item
@router.post("/", response_model=ItemResponse, status_code=status.HTTP_201_CREATED)
def create_item(payload: ItemCreate, db: Session = Depends(get_db)):
item = Item(**payload.model_dump())
db.add(item)
db.commit()
db.refresh(item)
return item
@router.put("/{item_id}", response_model=ItemResponse)
def update_item(item_id: int, payload: ItemCreate, db: Session = Depends(get_db)):
item = db.query(Item).filter(Item.id == item_id).first()
if not item:
raise HTTPException(status_code=404, detail="Item not found")
for key, value in payload.model_dump().items():
setattr(item, key, value)
db.commit()
db.refresh(item)
return item
@router.delete("/{item_id}", status_code=status.HTTP_204_NO_CONTENT)
def delete_item(item_id: int, db: Session = Depends(get_db)):
item = db.query(Item).filter(Item.id == item_id).first()
if not item:
raise HTTPException(status_code=404, detail="Item not found")
db.delete(item)
db.commit()Now wire everything together in app/main.py:
from fastapi import FastAPI
from .database import Base, engine
from .routers import items
Base.metadata.create_all(bind=engine)
app = FastAPI(title="Items API", version="1.0.0")
app.include_router(items.router)You now have a fully functional REST API with GET, POST, PUT, and DELETE endpoints — all validated, all documented automatically.
Adding Middleware and CORS
If you’re building a frontend that hits this API, you’ll need CORS. FastAPI makes it trivial:
from fastapi.middleware.cors import CORSMiddleware
app.add_middleware(
CORSMiddleware,
allow_origins=["https://yourfrontend.com"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)For production, be specific about allow_origins. Wildcards are fine in development, not in prod.
Environment Configuration
Hardcoding database URLs is a bad habit. Use pydantic-settings to handle environment variables cleanly:
pip install pydantic-settingsfrom pydantic_settings import BaseSettings
class Settings(BaseSettings):
database_url: str = "sqlite:///./items.db"
debug: bool = False
class Config:
env_file = ".env"
settings = Settings()Then replace the hardcoded URL in database.py with settings.database_url. Your app now reads from .env or real environment variables without any changes to the code.
Deploying Your FastAPI App
Once you understand how to build a Python API with FastAPI, deployment is the next hurdle. My preferred option for Python backends is Railway — it supports Python natively, reads your requirements.txt automatically, and you can go from a GitHub push to a live URL in under two minutes.
Create a Procfile in the project root:
web: uvicorn app.main:app --host 0.0.0.0 --port $PORTPush to GitHub, connect the repo to Railway, set your environment variables in the dashboard, and you’re live. For a production Postgres database, add a Railway Postgres plugin and update your DATABASE_URL environment variable — SQLAlchemy handles the rest with no code changes.
If you want a VPS for more control, DigitalOcean Droplets are a solid choice — run Uvicorn behind Nginx with a systemd service and you have a production-grade setup.
Testing Your API
FastAPI has built-in test support via httpx and the TestClient:
pip install pytest httpxfrom fastapi.testclient import TestClient
from app.main import app
client = TestClient(app)
def test_root():
response = client.get("/")
assert response.status_code == 200
def test_create_item():
payload = {"name": "Widget", "price": 9.99, "in_stock": True}
response = client.post("/items/", json=payload)
assert response.status_code == 201
assert response.json()["name"] == "Widget"Run with pytest. The test client handles the full request cycle including middleware without spinning up an actual server.
Going Deeper
This guide covers the core of how to build a Python API with FastAPI, but there’s more to explore: JWT authentication with python-jose, background tasks, WebSocket support, and dependency injection patterns for larger codebases. If you want a structured path through all of it, Udemy has several highly-rated FastAPI courses that go deep on auth, async patterns, and production architecture.
Wrapping Up
Knowing how to build a Python API with FastAPI is a genuinely useful skill in 2026. The framework handles the boilerplate — validation, serialization, docs — so you can focus on your actual business logic. The code in this guide is production-ready as a starting point: add auth, write your tests, swap SQLite for Postgres, and deploy. You’re done.
The full code from this article is available as a GitHub template if you want a head start. Start from what’s here, adapt the schemas to your domain, and ship something real.