0% found this document useful (0 votes)
2 views18 pages

FastAPI Complete Guide

FastAPI is a high-performance Python web framework designed for building APIs, utilizing standard Python type hints for data validation and automatic documentation generation. The guide includes comprehensive topics such as installation, API creation, authentication, dependency injection, and deployment strategies. FastAPI is noted for its speed, async support, and ease of use compared to other frameworks like Flask and Django REST.

Uploaded by

serkan
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
2 views18 pages

FastAPI Complete Guide

FastAPI is a high-performance Python web framework designed for building APIs, utilizing standard Python type hints for data validation and automatic documentation generation. The guide includes comprehensive topics such as installation, API creation, authentication, dependency injection, and deployment strategies. FastAPI is noted for its speed, async support, and ease of use compared to other frameworks like Flask and Django REST.

Uploaded by

serkan
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

FastAPI

Complete Reference Guide — Modern Python Web APIs

FastAPI is a high-performance Python web framework for building APIs. It is based on standard Python type hints, and
achieves speeds comparable to NodeJS and Go. This guide covers everything from installation to production
deployment.

Topics Covered

Installation & project setup

First API & running the server

Path parameters & query parameters

Request body & Pydantic models

Response models & status codes

Dependency injection

Authentication (OAuth2 / JWT / API keys)

Routers & application structure

Middleware & CORS

Background tasks & async

Database integration (SQLAlchemy / async)

File uploads & static files

Testing with pytest

Deployment (Docker / Uvicorn / Gunicorn)

Common options, tips & troubleshooting


1. What Is FastAPI?
FastAPI is a modern, fast web framework for building APIs with Python, based on ASGI (Asynchronous Server Gateway
Interface). It leverages Python type hints to automatically validate data, generate documentation, and provide IDE
autocompletion.

1.1 Key Features


Feature Detail

Performance On par with NodeJS & Go — powered by Starlette & Pydantic

Auto docs Swagger UI (/docs) and ReDoc (/redoc) generated automatically

Type safety Python type hints drive validation, serialization, IDE support

Async support First-class async/await — handles thousands of concurrent requests

Standards-based OpenAPI 3.x + JSON Schema — interoperable with any tooling

Dependency injection Clean, testable DI system built into the framework

1.2 FastAPI vs Flask vs Django REST


Aspect FastAPI Flask Django REST

Speed Very fast (ASGI) Moderate (WSGI) Moderate (WSGI)

Async Native Via extensions Via channels

Auto docs Built-in Manual/extension drf-spectacular

Validation Pydantic (auto) Manual/Marshmallow Serializers

Learning curve Low-Medium Low Medium-High

Best for APIs, microservices Simple APIs, apps Full-stack apps

2. Installation & Project Setup

2.1 Install FastAPI


# Minimal install
pip install fastapi

# With Uvicorn ASGI server (recommended)


pip install 'fastapi[standard]'
# Includes: uvicorn, pydantic, email-validator, python-multipart, etc.

# Or install components separately


pip install fastapi uvicorn[standard] pydantic

2.2 Recommended Project Layout


myproject/
app/
__init__.py
[Link] # FastAPI app instance + startup
[Link] # Settings / env vars
[Link] # Shared DI dependencies
models/
__init__.py
[Link] # SQLAlchemy models
schemas/
__init__.py
[Link] # Pydantic schemas
routers/
__init__.py
[Link] # APIRouter for /users
[Link]
services/
user_service.py
tests/
test_users.py
[Link]
Dockerfile

2.3 Virtual Environment (best practice)


python -m venv .venv
source .venv/bin/activate # Linux/macOS
.venv\Scripts\activate # Windows
pip install 'fastapi[standard]'
3. Your First FastAPI App

3.1 Minimal App ([Link])


from fastapi import FastAPI

app = FastAPI(
title='My API',
description='A sample FastAPI application',
version='1.0.0',
)

@[Link]('/')
def read_root():
return {'message': 'Hello, FastAPI!'}

@[Link]('/items/{item_id}')
def read_item(item_id: int, q: str | None = None):
return {'item_id': item_id, 'q': q}

3.2 Running the Server


# Development (auto-reload on file changes)
uvicorn [Link]:app --reload

# Custom host/port
uvicorn [Link]:app --host [Link] --port 8080 --reload

# Or run from Python


import uvicorn
if __name__ == '__main__':
[Link]('[Link]:app', host='[Link]', port=8000, reload=True)

3.3 Auto-Generated Docs


URL Tool Description

[Link] Swagger UI Interactive API explorer — try endpoints live

[Link] ReDoc Clean reference documentation

[Link] OpenAPI Raw OpenAPI 3.x JSON schema

4. Path & Query Parameters

4.1 Path Parameters


from fastapi import FastAPI, Path

app = FastAPI()

@[Link]('/users/{user_id}')
def get_user(user_id: int): # type enforced automatically
return {'user_id': user_id}

# With validation constraints


@[Link]('/items/{item_id}')
def get_item(
item_id: int = Path(gt=0, le=1000, description='Item ID 1-1000'),
):
return {'item_id': item_id}

4.2 Query Parameters


from fastapi import Query

@[Link]('/items/')
def list_items(
skip: int = 0,
limit: int = Query(default=10, ge=1, le=100),
search: str | None = Query(default=None, min_length=3),
):
return {'skip': skip, 'limit': limit, 'search': search}
# GET /items/?skip=0&limit;=20&search;=foo

4.3 Enum Path Parameters


from enum import Enum

class ModelName(str, Enum):


alexnet = 'alexnet'
resnet = 'resnet'
lenet = 'lenet'

@[Link]('/models/{model_name}')
def get_model(model_name: ModelName):
return {'model': model_name, 'value': model_name.value}

✓ FastAPI automatically returns 422 Unprocessable Entity with detailed errors when parameters fail validation.
5. Request Body & Pydantic Models

5.1 Basic Pydantic Schema


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

class ItemCreate(BaseModel):
name: str = Field(min_length=1, max_length=100)
description: Optional[str] = Field(default=None, max_length=500)
price: float = Field(gt=0, description='Price must be positive')
tax: float | None = None
tags: list[str] = []

class UserCreate(BaseModel):
email: EmailStr
username: str = Field(min_length=3, pattern=r'^[a-z0-9_]+$')
full_name: str | None = None
created_at: datetime = Field(default_factory=[Link])

class Config:
json_schema_extra = {
'example': {'email': 'user@[Link]', 'username': 'jdoe'}
}

5.2 Using Models in Endpoints


@[Link]('/items/', status_code=201)
def create_item(item: ItemCreate):
# item is fully validated, typed, and IDE-friendly
item_dict = item.model_dump() # Pydantic v2
item_dict['id'] = 1 # simulate DB insert
return item_dict

# Mixed: path + body + query


@[Link]('/items/{item_id}')
def update_item(item_id: int, item: ItemCreate, notify: bool = False):
return {'item_id': item_id, 'item': item, 'notify': notify}

5.3 Nested Models


class Address(BaseModel):
street: str
city: str
country: str = 'US'

class UserFull(BaseModel):
username: str
address: Address
aliases: list[str] = []

5.4 Pydantic Field Types Quick Reference


Type Import / Usage Notes

str, int, float Built-in Standard Python types


EmailStr pydantic[email] Validates email format

HttpUrl from pydantic import HttpUrl Validates URL

UUID from uuid import UUID UUID v4 supported

datetime / date from datetime import ... ISO 8601 auto-parsed

list[T] Built-in Typed lists

dict[str, T] Built-in Typed dicts

Literal['a','b'] from typing import Literal Fixed value set

Optional[T] from typing import Optional Same as T | None

6. Response Models & Status Codes

6.1 Response Model


from pydantic import BaseModel
from fastapi import FastAPI
from [Link] import JSONResponse

class UserOut(BaseModel): # only expose safe fields


id: int
username: str
email: str
# password is NOT here — never returned

class UserIn(UserOut):
password: str # extends UserOut for input

@[Link]('/users/', response_model=UserOut, status_code=201)


def create_user(user: UserIn):
# FastAPI strips any field not in UserOut automatically
return user

6.2 HTTP Status Codes


Code Constant Use

200 HTTP_200_OK Default success

201 HTTP_201_CREATED Resource created (POST)

204 HTTP_204_NO_CONTENT Delete / no body

400 HTTP_400_BAD_REQUEST Invalid input

401 HTTP_401_UNAUTHORIZED Not authenticated

403 HTTP_403_FORBIDDEN Authenticated but no permission

404 HTTP_404_NOT_FOUND Resource not found

409 HTTP_409_CONFLICT Duplicate resource

422 HTTP_422_UNPROCESSABLE Validation error (auto)


500 HTTP_500_INTERNAL_SERVER_ERROR Unexpected server error

6.3 Raising HTTP Exceptions


from fastapi import HTTPException, status

@[Link]('/users/{user_id}')
def get_user(user_id: int):
user = [Link](user_id) # hypothetical DB call
if not user:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=f'User {user_id} not found',
)
return user
7. Dependency Injection
FastAPI's DI system lets you declare shared logic (auth checks, DB sessions, pagination params) as reusable
dependencies injected automatically.

7.1 Simple Function Dependency


from fastapi import Depends

def common_pagination(skip: int = 0, limit: int = 20):


return {'skip': skip, 'limit': limit}

@[Link]('/items/')
def list_items(pagination: dict = Depends(common_pagination)):
return {'pagination': pagination}

7.2 Class-Based Dependency


class DBSession:
def __init__(self):
[Link] = get_database_connection() # your DB factory

def __del__(self):
[Link]()

@[Link]('/users/')
def list_users(session: DBSession = Depends()):
return [Link].query_all_users()

7.3 Dependency with Yield (context manager style)


from [Link] import Session
from [Link] import SessionLocal

def get_db():
db = SessionLocal()
try:
yield db # hand DB session to endpoint
finally:
[Link]() # always runs — even on exception

@[Link]('/users/{uid}')
def get_user(uid: int, db: Session = Depends(get_db)):
return [Link](User).filter([Link] == uid).first()

7.4 Dependency Chaining


def verify_token(token: str = Depends(oauth2_scheme)):
... # raises 401 if invalid
return payload

def get_current_user(payload = Depends(verify_token), db = Depends(get_db)):


return [Link](User).filter([Link] == payload['sub']).first()

@[Link]('/me')
def me(user = Depends(get_current_user)):
return user
✓ Use Annotated[T, Depends(...)] (Python 3.11+) for cleaner signatures.

8. Authentication

8.1 OAuth2 Password Flow + JWT


pip install python-jose[cryptography] passlib[bcrypt]

from [Link] import OAuth2PasswordBearer, OAuth2PasswordRequestForm


from jose import JWTError, jwt
from [Link] import CryptContext
from datetime import datetime, timedelta

SECRET_KEY = 'your-secret-key-change-in-production'
ALGORITHM = 'HS256'
ACCESS_TOKEN_EXPIRE_MINUTES = 30

pwd_context = CryptContext(schemes=['bcrypt'], deprecated='auto')


oauth2_scheme = OAuth2PasswordBearer(tokenUrl='/auth/token')

def create_access_token(data: dict, expires_delta: timedelta | None = None):


to_encode = [Link]()
expire = [Link]() + (expires_delta or timedelta(minutes=15))
to_encode.update({'exp': expire})
return [Link](to_encode, SECRET_KEY, algorithm=ALGORITHM)

@[Link]('/auth/token')
def login(form_data: OAuth2PasswordRequestForm = Depends()):
user = authenticate_user(form_data.username, form_data.password)
if not user:
raise HTTPException(status_code=401, detail='Invalid credentials')
token = create_access_token({'sub': [Link]},
timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES))
return {'access_token': token, 'token_type': 'bearer'}

8.2 API Key Authentication


from [Link] import APIKeyHeader
from fastapi import Security

api_key_header = APIKeyHeader(name='X-API-Key')

def require_api_key(api_key: str = Security(api_key_header)):


if api_key != settings.API_KEY:
raise HTTPException(status_code=403, detail='Invalid API key')
return api_key

@[Link]('/protected')
def protected_route(key = Depends(require_api_key)):
return {'status': 'authorized'}

✕ Never hardcode secrets. Use environment variables and a secrets manager in production.
9. Routers & Application Structure

9.1 Creating a Router (app/routers/[Link])


from fastapi import APIRouter, Depends, HTTPException, status
from [Link] import UserOut, UserCreate
from [Link] import get_db, get_current_user

router = APIRouter(
prefix='/users',
tags=['users'],
dependencies=[Depends(get_current_user)], # auth on all routes
responses={404: {'description': 'Not found'}},
)

@[Link]('/', response_model=list[UserOut])
def list_users(db = Depends(get_db)):
return [Link](User).all()

@[Link]('/{user_id}', response_model=UserOut)
def get_user(user_id: int, db = Depends(get_db)):
user = [Link](User).filter([Link] == user_id).first()
if not user:
raise HTTPException(status_code=404, detail='User not found')
return user

@[Link]('/', response_model=UserOut, status_code=201)


def create_user(user_in: UserCreate, db = Depends(get_db)):
...

@[Link]('/{user_id}', status_code=204)
def delete_user(user_id: int, db = Depends(get_db)):
...

9.2 Mounting Routers (app/[Link])


from fastapi import FastAPI
from [Link] import users, items, auth

app = FastAPI()

app.include_router([Link])
app.include_router([Link])
app.include_router([Link], prefix='/v1', tags=['items-v1'])

10. Middleware & CORS

10.1 CORS (Cross-Origin Resource Sharing)


from [Link] import CORSMiddleware

app.add_middleware(
CORSMiddleware,
allow_origins=['[Link] # list of allowed origins
allow_origin_regex=r'[Link]
allow_credentials=True,
allow_methods=['*'],
allow_headers=['*'],
max_age=3600,
)

■ Use specific origins in production — never allow_origins=['*'] with credentials.

10.2 Custom Middleware


import time
from fastapi import Request

@[Link]('http')
async def add_process_time_header(request: Request, call_next):
start = time.perf_counter()
response = await call_next(request)
duration = time.perf_counter() - start
[Link]['X-Process-Time'] = str(round(duration, 4))
return response

10.3 Trusted Host / GZip / HTTPs Redirect


from [Link] import TrustedHostMiddleware
from [Link] import GZipMiddleware
from [Link] import HTTPSRedirectMiddleware

app.add_middleware(TrustedHostMiddleware, allowed_hosts=['[Link]', '*.[Link]'])


app.add_middleware(GZipMiddleware, minimum_size=1000)
app.add_middleware(HTTPSRedirectMiddleware) # prod only
11. Async, Background Tasks & Lifespan

11.1 Async Endpoints


import httpx

# Use async def for I/O-bound work (DB calls, HTTP, files)
@[Link]('/external-data')
async def fetch_data():
async with [Link]() as client:
resp = await [Link]('[Link]
return [Link]()

# Use def for CPU-bound work (FastAPI runs it in a thread pool)


@[Link]('/compute')
def heavy_compute(data: dict):
result = run_heavy_ml_model(data) # blocking OK in def
return result

11.2 Background Tasks


from fastapi import BackgroundTasks

def send_welcome_email(email: str):


# runs after response is sent
send_email(email, subject='Welcome!')

@[Link]('/users/', status_code=201)
def create_user(user: UserCreate, bg: BackgroundTasks):
new_user = db_create_user(user)
bg.add_task(send_welcome_email, [Link])
return new_user # response returned immediately

11.3 Lifespan Events (startup / shutdown)


from contextlib import asynccontextmanager
from fastapi import FastAPI

@asynccontextmanager
async def lifespan(app: FastAPI):
# startup: runs before accepting requests
await [Link]()
load_ml_model()
yield # app runs here
# shutdown: runs after last request
await [Link]()

app = FastAPI(lifespan=lifespan)

12. Database Integration

12.1 SQLAlchemy (sync)


pip install sqlalchemy psycopg2-binary # PostgreSQL
pip install sqlalchemy aiosqlite # SQLite async

# app/[Link]
from sqlalchemy import create_engine
from [Link] import sessionmaker, DeclarativeBase

DATABASE_URL = 'postgresql://user:pass@localhost/mydb'

engine = create_engine(DATABASE_URL)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)

class Base(DeclarativeBase):
pass

# app/models/[Link]
from sqlalchemy import Column, Integer, String
from [Link] import Base

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)

12.2 Async SQLAlchemy


pip install sqlalchemy[asyncio] asyncpg

from [Link] import create_async_engine, AsyncSession


from [Link] import sessionmaker

DATABASE_URL = 'postgresql+asyncpg://user:pass@localhost/mydb'
engine = create_async_engine(DATABASE_URL, echo=True)
AsyncSessionLocal = sessionmaker(engine, class_=AsyncSession, expire_on_commit=False)

async def get_db():


async with AsyncSessionLocal() as session:
yield session
13. File Uploads & Static Files

13.1 File Upload


from fastapi import UploadFile, File
import aiofiles

@[Link]('/upload/')
async def upload_file(file: UploadFile = File(...)):
contents = await [Link]()
# Save to disk async:
async with [Link](f'uploads/{[Link]}', 'wb') as f:
await [Link](contents)
return {'filename': [Link], 'size': len(contents)}

# Multiple files:
@[Link]('/upload-many/')
async def upload_multiple(files: list[UploadFile] = File(...)):
return [{'name': [Link], 'type': f.content_type} for f in files]

13.2 Static Files


from [Link] import StaticFiles

[Link]('/static', StaticFiles(directory='static'), name='static')


# Access: [Link]

13.3 Streaming Response


from [Link] import StreamingResponse
import io

@[Link]('/download/')
def download_file():
content = generate_csv_data()
return StreamingResponse(
[Link](content),
media_type='text/csv',
headers={'Content-Disposition': 'attachment; filename=[Link]'},
)

14. Testing with pytest


pip install httpx pytest pytest-asyncio

14.1 Basic Tests


from [Link] import TestClient
from [Link] import app

client = TestClient(app)

def test_read_root():
resp = [Link]('/')
assert resp.status_code == 200
assert [Link]() == {'message': 'Hello, FastAPI!'}
def test_create_item():
resp = [Link]('/items/', json={'name': 'Widget', 'price': 9.99})
assert resp.status_code == 201
assert [Link]()['name'] == 'Widget'

def test_validation_error():
resp = [Link]('/items/', json={'name': '', 'price': -1})
assert resp.status_code == 422

14.2 Override Dependencies in Tests


def override_get_db():
db = TestingSessionLocal()
try:
yield db
finally:
[Link]()

app.dependency_overrides[get_db] = override_get_db

def test_get_users():
resp = [Link]('/users/')
assert resp.status_code == 200

14.3 Async Tests


import pytest
from httpx import AsyncClient

@[Link]
async def test_async_endpoint():
async with AsyncClient(app=app, base_url='[Link] as ac:
resp = await [Link]('/async-endpoint')
assert resp.status_code == 200
15. Deployment

15.1 Production Server — Gunicorn + Uvicorn Workers


pip install gunicorn uvicorn[standard]

# Run with multiple workers (recommended: 2*CPU + 1)


gunicorn [Link]:app \
--workers 4 \
--worker-class [Link] \
--bind [Link]:8000 \
--timeout 120 \
--access-logfile -

15.2 Dockerfile
FROM python:3.12-slim

WORKDIR /app

COPY [Link] .
RUN pip install --no-cache-dir -r [Link]

COPY . .

EXPOSE 8000

CMD ['gunicorn', '[Link]:app',


'--workers', '4',
'--worker-class', '[Link]',
'--bind', '[Link]:8000']

15.3 Environment Configuration


pip install pydantic-settings

# app/[Link]
from pydantic_settings import BaseSettings

class Settings(BaseSettings):
app_name: str = 'My API'
debug: bool = False
database_url: str
secret_key: str
api_key: str

class Config:
env_file = '.env'

settings = Settings()

# .env file (never commit to git!)


DATABASE_URL=postgresql://user:pass@db:5432/mydb
SECRET_KEY=super-secret-key-here

✕ Never commit .env files or secrets to version control.


16. Quick Reference Card
Task Code / Command

Install pip install 'fastapi[standard]'

Run dev server uvicorn [Link]:app --reload

Swagger docs [Link]

Path param @[Link]('/items/{id}') def f(id: int)

Query param def f(skip: int = 0, limit: int = 10)

Request body def f(item: ItemModel)

Status code @[Link]('/', status_code=201)

Raise 404 raise HTTPException(status_code=404, detail='...')

Response model @[Link]('/', response_model=UserOut)

Dependency def f(db = Depends(get_db))

Background task bg.add_task(fn, arg)

CORS app.add_middleware(CORSMiddleware, ...)

Include router app.include_router(router, prefix='/v1')

File upload async def f(file: UploadFile = File(...))

Static files [Link]('/static', StaticFiles(directory='static'))

Override dep in test app.dependency_overrides[dep] = mock_dep

Test client client = TestClient(app)

Prod server gunicorn [Link]:app --worker-class [Link]

Config from env class Settings(BaseSettings): ...

FastAPI Complete Guide — based on FastAPI 0.11x / Pydantic v2. Full docs at [Link]

You might also like