FastAPI Framework

Introduction to FastAPI

FastAPI is a modern, fast (high-performance) web framework for building APIs with Python 3.6+ based on standard Python type hints. It's designed to be easy to use, fast to code, and ready for production.

Key Features:

  • Fast: Very high performance, on par with NodeJS and Go
  • Fast to code: Increase the speed of development by 200% to 300%
  • Automatic API documentation
  • Based on Python type hints
  • Modern Python features (async/await)
  • Automatic data validation

Why FastAPI?

FastAPI combines the best of modern Python features with automatic API documentation and validation, making it ideal for building robust, high-performance APIs.

Installation and Setup

Basic Setup

# Create virtual environment
python -m venv venv

# Activate virtual environment
# Windows:
venv\Scripts\activate
# Unix/MacOS:
source venv/bin/activate

# Install FastAPI and Uvicorn
pip install fastapi uvicorn

# Basic FastAPI application
from fastapi import FastAPI

app = FastAPI()

@app.get("/")
async def root():
    return {"message": "Hello World"}

# Run with: uvicorn main:app --reload

Project Structure

myapi/
    ├── app/
    │   ├── __init__.py
    │   ├── main.py
    │   ├── dependencies.py
    │   ├── routers/
    │   ├── models/
    │   └── schemas/
    ├── tests/
    ├── alembic/
    ├── requirements.txt
    └── README.md

Core Concepts

Pydantic Models

from pydantic import BaseModel
from typing import Optional

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

Request and Response Models

@app.post("/items/", response_model=Item)
async def create_item(item: Item):
    return item

@app.get("/items/{item_id}", response_model=Item)
async def read_item(item_id: int):
    return {"name": "Example", "price": 10.5}

Path Operations

Basic Routing

from fastapi import Path, Query

@app.get("/items/{item_id}")
async def read_item(
    item_id: int = Path(..., title="The ID of the item"),
    q: str = Query(None, max_length=50)
):
    return {"item_id": item_id, "q": q}

Request Bodies

from fastapi import Body

@app.post("/items/{item_id}")
async def update_item(
    item_id: int,
    item: Item,
    user: User,
    importance: int = Body(...)
):
    results = {"item_id": item_id, "item": item}
    return results

Data Validation

Field Validation

from pydantic import BaseModel, Field

class Item(BaseModel):
    name: str = Field(..., min_length=1, max_length=50)
    price: float = Field(..., gt=0)
    description: Optional[str] = Field(None, max_length=1000)
    tags: List[str] = Field(default_factory=list)

Custom Validators

from pydantic import validator

class User(BaseModel):
    username: str
    password: str
    password2: str

    @validator('password2')
    def passwords_match(cls, v, values):
        if 'password' in values and v != values['password']:
            raise ValueError('passwords do not match')
        return v

Database Integration

SQLAlchemy Setup

from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker

SQLALCHEMY_DATABASE_URL = "postgresql://user:password@localhost/db"

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

Database Models

from sqlalchemy import Column, Integer, String

class DBItem(Base):
    __tablename__ = "items"

    id = Column(Integer, primary_key=True, index=True)
    name = Column(String, index=True)
    description = Column(String)
    price = Column(Float)

Authentication and Security

OAuth2 with JWT

from fastapi.security import OAuth2PasswordBearer
from jose import JWTError, jwt

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")

async def get_current_user(token: str = Depends(oauth2_scheme)):
    credentials_exception = HTTPException(
        status_code=401,
        detail="Could not validate credentials"
    )
    try:
        payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
        username: str = payload.get("sub")
        if username is None:
            raise credentials_exception
        return username
    except JWTError:
        raise credentials_exception

Protected Routes

@app.get("/users/me")
async def read_users_me(
    current_user: User = Depends(get_current_user)
):
    return current_user

Deployment

Production Setup

Deployment Checklist:

  • Use Uvicorn with Gunicorn
  • Configure CORS
  • Set up proper logging
  • Enable HTTPS
  • Use environment variables
# Gunicorn configuration
# gunicorn.conf.py
workers_per_core = 1
cores = multiprocessing.cpu_count()
default_web_concurrency = workers_per_core * cores

# CORS setup
from fastapi.middleware.cors import CORSMiddleware

app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)