What is FastAPI?

FastAPI is a modern, high-performance Python web framework for building APIs. It's designed to be fast to code, fast to run, and to reduce bugs through type hints and automatic validation.

Created by Sebastián Ramírez in 2018, FastAPI has quickly become one of the most popular Python frameworks due to its speed, developer experience, and modern features.

Why FastAPI?

  • Fast: One of the fastest Python frameworks (on par with Node.js and Go)
  • Fast to code: 200-300% faster development speed
  • Fewer bugs: Type hints catch errors before runtime
  • Intuitive: Great editor support with autocomplete
  • Easy: Designed to be easy to use and learn
  • Standards-based: Built on OpenAPI (Swagger) and JSON Schema
  • Automatic docs: Interactive API documentation generated automatically
  • Modern Python: Uses async/await for high concurrency

Getting Started

# Install FastAPI and Uvicorn (ASGI server)
pip install fastapi uvicorn

# Create main.py
from fastapi import FastAPI

app = FastAPI()

@app.get("/")
def read_root():
    return {"Hello": "World"}

@app.get("/items/{item_id}")
def read_item(item_id: int, q: str = None):
    return {"item_id": item_id, "q": q}

# Run the server
# uvicorn main:app --reload

# Visit:
# http://127.0.0.1:8000        - Your API
# http://127.0.0.1:8000/docs   - Swagger UI
# http://127.0.0.1:8000/redoc  - ReDoc

Path Parameters and Query Parameters

from fastapi import FastAPI, Query, Path
from typing import Optional

app = FastAPI()

# Path parameter
@app.get("/users/{user_id}")
def get_user(user_id: int):  # Automatically validated as integer
    return {"user_id": user_id}

# Query parameters
@app.get("/items/")
def list_items(
    skip: int = 0,
    limit: int = 10,
    search: Optional[str] = None  # Optional parameter
):
    return {"skip": skip, "limit": limit, "search": search}

# Validation with Query and Path
@app.get("/products/{product_id}")
def get_product(
    product_id: int = Path(..., gt=0, description="Product ID must be positive"),
    q: str = Query(None, min_length=3, max_length=50)
):
    return {"product_id": product_id, "query": q}

# Multiple path parameters
@app.get("/users/{user_id}/posts/{post_id}")
def get_user_post(user_id: int, post_id: int):
    return {"user_id": user_id, "post_id": post_id}

Request Body with Pydantic

FastAPI uses Pydantic for data validation and serialization:

from fastapi import FastAPI
from pydantic import BaseModel, Field, EmailStr
from typing import Optional, List
from datetime import datetime

app = FastAPI()

# Define data models
class UserBase(BaseModel):
    email: EmailStr
    username: str = Field(..., min_length=3, max_length=50)

class UserCreate(UserBase):
    password: str = Field(..., min_length=8)

class UserResponse(UserBase):
    id: int
    created_at: datetime

    class Config:
        from_attributes = True  # For ORM models

class Item(BaseModel):
    name: str
    description: Optional[str] = None
    price: float = Field(..., gt=0)
    tax: Optional[float] = None
    tags: List[str] = []

# Use in endpoints
@app.post("/users/", response_model=UserResponse)
def create_user(user: UserCreate):
    # user is automatically validated
    # Returns only fields in UserResponse (password excluded)
    return {
        "id": 1,
        "email": user.email,
        "username": user.username,
        "created_at": datetime.now()
    }

@app.post("/items/")
def create_item(item: Item):
    item_dict = item.dict()
    if item.tax:
        item_dict["price_with_tax"] = item.price + item.tax
    return item_dict

@app.put("/items/{item_id}")
def update_item(item_id: int, item: Item):
    return {"item_id": item_id, **item.dict()}

Async/Await for High Performance

FastAPI is built on ASGI and supports async operations:

from fastapi import FastAPI
import httpx
import asyncio

app = FastAPI()

# Async endpoint
@app.get("/async-example")
async def async_example():
    # Non-blocking I/O
    async with httpx.AsyncClient() as client:
        response = await client.get("https://api.github.com")
        return response.json()

# Concurrent requests
@app.get("/fetch-multiple")
async def fetch_multiple():
    async with httpx.AsyncClient() as client:
        # Fetch multiple URLs concurrently
        tasks = [
            client.get("https://api.github.com/users/python"),
            client.get("https://api.github.com/users/fastapi"),
        ]
        responses = await asyncio.gather(*tasks)
        return [r.json() for r in responses]

# When to use async
# Use async def when:
# - Making HTTP requests (httpx, aiohttp)
# - Database queries (asyncpg, databases)
# - File I/O (aiofiles)

# Use regular def when:
# - CPU-bound operations
# - Calling sync libraries
# FastAPI handles both correctly

Dependency Injection

FastAPI has a powerful dependency injection system:

from fastapi import FastAPI, Depends, HTTPException, status
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials

app = FastAPI()
security = HTTPBearer()

# Simple dependency
def get_db():
    db = DatabaseSession()
    try:
        yield db
    finally:
        db.close()

# Using dependencies
@app.get("/items/")
def read_items(db: Session = Depends(get_db)):
    return db.query(Item).all()

# Authentication dependency
def get_current_user(credentials: HTTPAuthorizationCredentials = Depends(security)):
    token = credentials.credentials
    user = verify_token(token)  # Your verification logic
    if not user:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Invalid authentication"
        )
    return user

@app.get("/protected")
def protected_route(user: User = Depends(get_current_user)):
    return {"message": f"Hello, {user.username}"}

# Class-based dependencies
class Pagination:
    def __init__(self, skip: int = 0, limit: int = 10):
        self.skip = skip
        self.limit = limit

@app.get("/users/")
def list_users(pagination: Pagination = Depends()):
    return {"skip": pagination.skip, "limit": pagination.limit}

Database Integration with SQLAlchemy

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

DATABASE_URL = "postgresql://user:pass@localhost/dbname"

engine = create_engine(DATABASE_URL)
SessionLocal = sessionmaker(bind=engine)
Base = declarative_base()

# models.py
from sqlalchemy import Column, Integer, String, DateTime
from database import Base
from datetime import datetime

class User(Base):
    __tablename__ = "users"

    id = Column(Integer, primary_key=True, index=True)
    email = Column(String, unique=True, index=True)
    username = Column(String, unique=True)
    hashed_password = Column(String)
    created_at = Column(DateTime, default=datetime.utcnow)

# main.py
from fastapi import FastAPI, Depends, HTTPException
from sqlalchemy.orm import Session
from database import SessionLocal, engine
import models

models.Base.metadata.create_all(bind=engine)

app = FastAPI()

def get_db():
    db = SessionLocal()
    try:
        yield db
    finally:
        db.close()

@app.post("/users/")
def create_user(user: UserCreate, db: Session = Depends(get_db)):
    db_user = models.User(
        email=user.email,
        username=user.username,
        hashed_password=hash_password(user.password)
    )
    db.add(db_user)
    db.commit()
    db.refresh(db_user)
    return db_user

@app.get("/users/{user_id}")
def get_user(user_id: int, db: Session = Depends(get_db)):
    user = db.query(models.User).filter(models.User.id == user_id).first()
    if not user:
        raise HTTPException(status_code=404, detail="User not found")
    return user

Error Handling

from fastapi import FastAPI, HTTPException, Request
from fastapi.responses import JSONResponse
from pydantic import BaseModel

app = FastAPI()

# Using HTTPException
@app.get("/items/{item_id}")
def get_item(item_id: int):
    if item_id not in items_db:
        raise HTTPException(
            status_code=404,
            detail="Item not found",
            headers={"X-Error": "Item missing"}
        )
    return items_db[item_id]

# Custom exception
class ItemNotFoundError(Exception):
    def __init__(self, item_id: int):
        self.item_id = item_id

@app.exception_handler(ItemNotFoundError)
async def item_not_found_handler(request: Request, exc: ItemNotFoundError):
    return JSONResponse(
        status_code=404,
        content={"message": f"Item {exc.item_id} not found"}
    )

# Global exception handler
@app.exception_handler(Exception)
async def global_exception_handler(request: Request, exc: Exception):
    return JSONResponse(
        status_code=500,
        content={"message": "Internal server error"}
    )

Background Tasks

from fastapi import FastAPI, BackgroundTasks

app = FastAPI()

def send_email(email: str, message: str):
    # Simulate sending email
    import time
    time.sleep(5)  # This won't block the response
    print(f"Email sent to {email}")

def write_log(message: str):
    with open("log.txt", "a") as f:
        f.write(f"{message}\n")

@app.post("/send-notification/")
async def send_notification(
    email: str,
    background_tasks: BackgroundTasks
):
    # Add tasks to run in background
    background_tasks.add_task(send_email, email, "Welcome!")
    background_tasks.add_task(write_log, f"Notification sent to {email}")

    # Response returned immediately
    return {"message": "Notification will be sent"}

API Documentation

FastAPI automatically generates interactive documentation:

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI(
    title="My API",
    description="This is my awesome API",
    version="1.0.0",
    docs_url="/docs",      # Swagger UI
    redoc_url="/redoc",    # ReDoc
)

class Item(BaseModel):
    """An item in the inventory."""
    name: str
    price: float

    class Config:
        json_schema_extra = {
            "example": {
                "name": "Widget",
                "price": 9.99
            }
        }

@app.post(
    "/items/",
    summary="Create an item",
    description="Create a new item with name and price",
    response_description="The created item",
    tags=["items"]
)
def create_item(item: Item):
    """
    Create an item with the following info:

    - **name**: Item name (required)
    - **price**: Item price (required)
    """
    return item

# Visit /docs for Swagger UI
# Visit /redoc for ReDoc

Project Structure

my_fastapi_app/
├── app/
│   ├── __init__.py
│   ├── main.py              # FastAPI app instance
│   ├── config.py            # Settings and configuration
│   ├── database.py          # Database connection
│   ├── models/              # SQLAlchemy models
│   │   ├── __init__.py
│   │   └── user.py
│   ├── schemas/             # Pydantic models
│   │   ├── __init__.py
│   │   └── user.py
│   ├── routers/             # API routes
│   │   ├── __init__.py
│   │   ├── users.py
│   │   └── items.py
│   ├── services/            # Business logic
│   │   └── user_service.py
│   └── dependencies/        # Dependency injection
│       └── auth.py
├── tests/
│   └── test_main.py
├── requirements.txt
└── Dockerfile

FastAPI vs Flask vs Django

                FastAPI         Flask           Django
────────────────────────────────────────────────────────────────
Speed           Very Fast       Moderate        Moderate
Async           Native          Extension       3.1+
Type Hints      Core Feature    Optional        Optional
Auto Docs       Yes             No              No
ORM             SQLAlchemy      SQLAlchemy      Django ORM
Learning        Easy            Very Easy       Moderate
Best For        APIs            Simple Apps     Full Apps

Use FastAPI when:
- Building high-performance APIs
- Need automatic documentation
- Want type safety and validation
- Modern async Python

Use Flask when:
- Simple applications
- Learning web development
- Maximum flexibility needed

Use Django when:
- Need admin interface
- Full-featured web apps
- Rapid development with conventions

Master FastAPI with Expert Mentorship

Our Full Stack Python program covers FastAPI in depth. Learn to build high-performance APIs with automatic documentation, validation, and authentication with personalized guidance.

Explore Full Stack Python Program

Related Articles