What is FastAPI
FastAPI — a modern high‑performance web framework for building APIs in Python. Developed by Sebastián Ramírez, this framework is built on OpenAPI and JSON Schema standards, providing automatic documentation generation and data typing.
FastAPI leverages the latest Python features, including type hints and asynchronous programming, making it one of the fastest Python frameworks for API development. In terms of performance it rivals Node.js and Go, making it an excellent choice for high‑load applications.
Key Features of FastAPI
High Performance
FastAPI is built on Starlette for the web layer and Pydantic for data validation. This delivers performance comparable to the fastest Python frameworks. The framework fully supports ASGI (Asynchronous Server Gateway Interface), allowing it to handle thousands of concurrent connections.
Automatic Documentation Generation
One of the main features of FastAPI is automatic generation of interactive API documentation. The framework creates documentation in OpenAPI (Swagger) format and provides two interfaces:
- Swagger UI — available at
/docs - ReDoc — available at
/redoc
Full Type Support
FastAPI uses standard Python type annotations for data validation, serialization, and automatic documentation generation. This ensures excellent IDE autocomplete support and reduces the number of bugs during development.
Built‑in Data Validation
Thanks to integration with Pydantic, FastAPI automatically validates incoming data, converts types, and returns clear error messages. This greatly simplifies development and improves application reliability.
Asynchronous Out of the Box
FastAPI fully supports asynchronous programming with async/await. This enables the creation of high‑throughput applications capable of handling many simultaneous requests.
Installation and Basic Setup
Installing FastAPI
To install FastAPI and all required dependencies, run the following command:
pip install fastapi[all]
This command installs FastAPI together with Uvicorn (ASGI server) and other useful dependencies.
For a minimal installation, use:
pip install fastapi
pip install uvicorn[standard]
Creating Your First Application
Create a file main.py with a basic app:
from fastapi import FastAPI
app = FastAPI(
title="My FastAPI Project",
description="This is an example FastAPI application",
version="1.0.0"
)
@app.get("/")
async def root():
return {"message": "Welcome to FastAPI!"}
@app.get("/health")
async def health_check():
return {"status": "ok"}
Running the Application
Start the development server with Uvicorn:
uvicorn main:app --reload --host 0.0.0.0 --port 8000
Run options:
--reload— automatic reload on code changes--host 0.0.0.0— accept connections from any IP address--port 8000— port for the server
Working with HTTP Methods
FastAPI supports all standard HTTP methods via corresponding decorators:
GET Requests
@app.get("/items/{item_id}")
async def get_item(item_id: int, q: str = None):
return {"item_id": item_id, "q": q}
@app.get("/items/")
async def get_items(skip: int = 0, limit: int = 10):
return {"skip": skip, "limit": limit}
POST Requests
from pydantic import BaseModel
class Item(BaseModel):
name: str
price: float
is_offer: bool = False
@app.post("/items/")
async def create_item(item: Item):
return {"item": item, "message": "Item created"}
PUT and PATCH Requests
@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
return {"item_id": item_id, "item": item}
@app.patch("/items/{item_id}")
async def update_item_partial(item_id: int, item: dict):
return {"item_id": item_id, "updated_fields": item}
DELETE Requests
@app.delete("/items/{item_id}")
async def delete_item(item_id: int):
return {"message": f"Item {item_id} deleted"}
Working with Query Parameters
Path Parameters
from fastapi import Path
@app.get("/items/{item_id}")
async def get_item(
item_id: int = Path(..., title="Item ID", ge=1)
):
return {"item_id": item_id}
Query Parameters
from fastapi import Query
@app.get("/items/")
async def get_items(
q: str = Query(None, min_length=3, max_length=50),
skip: int = Query(0, ge=0),
limit: int = Query(10, ge=1, le=100)
):
return {"q": q, "skip": skip, "limit": limit}
Working with Headers
from fastapi import Header
@app.get("/items/")
async def get_items(
user_agent: str = Header(None),
x_token: str = Header(None, alias="X-Token")
):
return {"User-Agent": user_agent, "X-Token": x_token}
Working with Cookies
from fastapi import Cookie
@app.get("/items/")
async def get_items(ads_id: str = Cookie(None)):
return {"ads_id": ads_id}
Data Models with Pydantic
Creating Basic Models
from pydantic import BaseModel, Field
from typing import Optional
from datetime import datetime
class User(BaseModel):
id: Optional[int] = None
name: str = Field(..., min_length=1, max_length=100)
email: str = Field(..., regex=r'^[\w\.-]+@[\w\.-]+\.\w+$')
age: int = Field(..., ge=0, le=150)
created_at: datetime = Field(default_factory=datetime.now)
class Config:
schema_extra = {
"example": {
"name": "Ivan Ivanov",
"email": "ivan@example.com",
"age": 30
}
}
Nested Models
from typing import List
class Address(BaseModel):
street: str
city: str
country: str
class UserWithAddress(BaseModel):
name: str
email: str
addresses: List[Address]
Response Models
class UserResponse(BaseModel):
id: int
name: str
email: str
created_at: datetime
@app.post("/users/", response_model=UserResponse)
async def create_user(user: User):
# User creation logic
return UserResponse(
id=1,
name=user.name,
email=user.email,
created_at=datetime.now()
)
Asynchronous Programming
Async Handlers
import asyncio
import aiohttp
@app.get("/async-data")
async def get_async_data():
async with aiohttp.ClientSession() as session:
async with session.get('https://api.example.com/data') as response:
data = await response.json()
return data
@app.get("/delayed")
async def delayed_response():
await asyncio.sleep(2)
return {"message": "Response after 2 seconds"}
Working with Async Databases
import asyncpg
from databases import Database
DATABASE_URL = "postgresql://user:password@localhost/database"
database = Database(DATABASE_URL)
@app.on_event("startup")
async def startup():
await database.connect()
@app.on_event("shutdown")
async def shutdown():
await database.disconnect()
@app.get("/users/{user_id}")
async def get_user(user_id: int):
query = "SELECT * FROM users WHERE id = :user_id"
user = await database.fetch_one(query, {"user_id": user_id})
return user
Error Handling
Standard HTTP Exceptions
from fastapi import HTTPException, status
@app.get("/items/{item_id}")
async def get_item(item_id: int):
if item_id == 0:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Item not found"
)
return {"item_id": item_id}
Custom Exception Handlers
from fastapi import Request
from fastapi.responses import JSONResponse
class CustomException(Exception):
def __init__(self, name: str):
self.name = name
@app.exception_handler(CustomException)
async def custom_exception_handler(request: Request, exc: CustomException):
return JSONResponse(
status_code=418,
content={"message": f"An error occurred: {exc.name}"}
)
Dependencies and Dependency Injection
Simple Dependencies
from fastapi import Depends
async def common_parameters(q: str = None, skip: int = 0, limit: int = 100):
return {"q": q, "skip": skip, "limit": limit}
@app.get("/items/")
async def get_items(commons: dict = Depends(common_parameters)):
return commons
Stateful Dependencies
class DatabaseManager:
def __init__(self):
self.connection = None
async def connect(self):
# Connect to the database
pass
async def disconnect(self):
# Disconnect from the database
pass
db_manager = DatabaseManager()
async def get_db():
return db_manager
@app.get("/items/")
async def get_items(db: DatabaseManager = Depends(get_db)):
return {"database": "connected"}
Authentication and Authorization
OAuth2 with JWT
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from passlib.context import CryptContext
from jose import JWTError, jwt
from datetime import datetime, timedelta
from typing import Optional
SECRET_KEY = "your-secret-key"
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 30
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
def verify_password(plain_password, hashed_password):
return pwd_context.verify(plain_password, hashed_password)
def get_password_hash(password):
return pwd_context.hash(password)
def create_access_token(data: dict, expires_delta: Optional[timedelta] = None):
to_encode = data.copy()
if expires_delta:
expire = datetime.utcnow() + expires_delta
else:
expire = datetime.utcnow() + timedelta(minutes=15)
to_encode.update({"exp": expire})
encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
return encoded_jwt
async def get_current_user(token: str = Depends(oauth2_scheme)):
credentials_exception = HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Could not validate credentials",
headers={"WWW-Authenticate": "Bearer"},
)
try:
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
username: str = payload.get("sub")
if username is None:
raise credentials_exception
except JWTError:
raise credentials_exception
return username
@app.post("/token")
async def login(form_data: OAuth2PasswordRequestForm = Depends()):
# User verification
access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
access_token = create_access_token(
data={"sub": form_data.username}, expires_delta=access_token_expires
)
return {"access_token": access_token, "token_type": "bearer"}
@app.get("/protected")
async def protected_route(current_user: str = Depends(get_current_user)):
return {"message": f"Hello {current_user}"}
Middleware and CORS
Adding CORS
from fastapi.middleware.cors import CORSMiddleware
app.add_middleware(
CORSMiddleware,
allow_origins=["http://localhost:3000", "https://myapp.com"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
Custom Middleware
from fastapi import Request
import time
@app.middleware("http")
async def add_process_time_header(request: Request, call_next):
start_time = time.time()
response = await call_next(request)
process_time = time.time() - start_time
response.headers["X-Process-Time"] = str(process_time)
return response
Working with Databases
SQLAlchemy with FastAPI
from sqlalchemy import create_engine, Column, Integer, String
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker, Session
SQLALCHEMY_DATABASE_URL = "sqlite:///./test.db"
engine = create_engine(SQLALCHEMY_DATABASE_URL)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
Base = declarative_base()
class User(Base):
__tablename__ = "users"
id = Column(Integer, primary_key=True, index=True)
name = Column(String, index=True)
email = Column(String, unique=True, index=True)
Base.metadata.create_all(bind=engine)
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
@app.post("/users/")
async def create_user(user: User, db: Session = Depends(get_db)):
db_user = User(name=user.name, email=user.email)
db.add(db_user)
db.commit()
db.refresh(db_user)
return db_user
Project Structure
Recommended Structure for Large Projects
project/
├── main.py # Entry point
├── core/
│ ├── __init__.py
│ ├── config.py # Configuration
│ ├── security.py # Security
│ └── database.py # DB connection
├── models/
│ ├── __init__.py
│ ├── user.py # User models
│ └── item.py # Item models
├── schemas/
│ ├── __init__.py
│ ├── user.py # Pydantic schemas
│ └── item.py
├── api/
│ ├── __init__.py
│ ├── deps.py # Dependencies
│ └── v1/
│ ├── __init__.py
│ ├── endpoints/
│ │ ├── __init__.py
│ │ ├── users.py
│ │ └── items.py
│ └── api.py
├── tests/
│ ├── __init__.py
│ └── test_main.py
└── requirements.txt
Using APIRouter
from fastapi import APIRouter
router = APIRouter(
prefix="/api/v1",
tags=["items"],
responses={404: {"description": "Not found"}}
)
@router.get("/items/")
async def get_items():
return [{"item_id": 1}]
@router.get("/items/{item_id}")
async def get_item(item_id: int):
return {"item_id": item_id}
# In main.py
app.include_router(router)
Testing FastAPI Applications
Basic Testing
from fastapi.testclient import TestClient
import pytest
client = TestClient(app)
def test_read_main():
response = client.get("/")
assert response.status_code == 200
assert response.json() == {"message": "Welcome to FastAPI!"}
def test_create_item():
response = client.post(
"/items/",
json={"name": "Test Item", "price": 10.5, "is_offer": True}
)
assert response.status_code == 200
assert response.json()["item"]["name"] == "Test Item"
@pytest.mark.asyncio
async def test_async_endpoint():
response = client.get("/async-data")
assert response.status_code == 200
Testing with a Database
import pytest
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
SQLALCHEMY_DATABASE_URL = "sqlite:///./test.db"
engine = create_engine(SQLALCHEMY_DATABASE_URL)
TestingSessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
def override_get_db():
try:
db = TestingSessionLocal()
yield db
finally:
db.close()
app.dependency_overrides[get_db] = override_get_db
@pytest.fixture
def client():
with TestClient(app) as c:
yield c
Deploying FastAPI Applications
Using Docker
FROM python:3.9
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
Production with Gunicorn
pip install gunicorn
gunicorn main:app -w 4 -k uvicorn.workers.UvicornWorker --bind 0.0.0.0:8000
Nginx Configuration
server {
listen 80;
server_name yourdomain.com;
location / {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
WebSocket Support
from fastapi import WebSocket
@app.websocket("/ws")
async def websocket_endpoint(websocket: WebSocket):
await websocket.accept()
while True:
data = await websocket.receive_text()
await websocket.send_text(f"Message: {data}")
Background Tasks
from fastapi import BackgroundTasks
def write_notification(email: str, message: str):
with open("log.txt", mode="w") as email_file:
content = f"notification for {email}: {message}"
email_file.write(content)
@app.post("/send-notification/")
async def send_notification(email: str, background_tasks: BackgroundTasks):
background_tasks.add_task(write_notification, email, "some notification")
return {"message": "Notification sent in the background"}
Complete FastAPI Methods and Functions Reference
| Category | Method/Function | Description | Example Usage |
|---|---|---|---|
| Application Creation | FastAPI() |
Create an application instance | app = FastAPI(title="My API") |
app.title |
API title | app.title = "My API" |
|
app.description |
API description | app.description = "API description" |
|
app.version |
API version | app.version = "1.0.0" |
|
| HTTP Methods | @app.get() |
GET requests | @app.get("/items/") |
@app.post() |
POST requests | @app.post("/items/") |
|
@app.put() |
PUT requests | @app.put("/items/{id}") |
|
@app.delete() |
DELETE requests | @app.delete("/items/{id}") |
|
@app.patch() |
PATCH requests | @app.patch("/items/{id}") |
|
@app.head() |
HEAD requests | @app.head("/items/") |
|
@app.options() |
OPTIONS requests | @app.options("/items/") |
|
| Parameters | Path() |
Path parameters | Path(..., ge=1) |
Query() |
Query parameters | Query(None, max_length=50) |
|
Body() |
Request body | Body(...) |
|
Form() |
Form data | Form(...) |
|
File() |
File uploads | File(...) |
|
Header() |
HTTP headers | Header(None) |
|
Cookie() |
Cookies | Cookie(None) |
|
| Data Models | BaseModel |
Base Pydantic model | class Item(BaseModel): ... |
Field() |
Model field with validation | Field(..., min_length=1) |
|
validator() |
Custom validator | @validator('email') |
|
| Responses | JSONResponse |
JSON response | JSONResponse(content={}) |
HTMLResponse |
HTML response | HTMLResponse(content="<html>") |
|
PlainTextResponse |
Plain text response | PlainTextResponse("text") |
|
RedirectResponse |
Redirect | RedirectResponse(url="/") |
|
FileResponse |
File response | FileResponse("file.pdf") |
|
| Status Codes | status.HTTP_200_OK |
HTTP 200 | status_code=status.HTTP_200_OK |
status.HTTP_201_CREATED |
HTTP 201 | status_code=status.HTTP_201_CREATED |
|
status.HTTP_404_NOT_FOUND |
HTTP 404 | status_code=status.HTTP_404_NOT_FOUND |
|
status.HTTP_422_UNPROCESSABLE_ENTITY |
HTTP 422 | status_code=status.HTTP_422_UNPROCESSABLE_ENTITY |
|
| Exceptions | HTTPException |
HTTP exception | HTTPException(status_code=404) |
RequestValidationError |
Validation error | Handled automatically | |
| Dependencies | Depends() |
Dependency injection | Depends(get_current_user) |
Security() |
Security dependencies | Security(oauth2_scheme) |
|
| Security | OAuth2PasswordBearer |
OAuth2 authentication | OAuth2PasswordBearer(tokenUrl="token") |
OAuth2PasswordRequestForm |
Login form | Depends(OAuth2PasswordRequestForm) |
|
HTTPBasic |
HTTP Basic Auth | HTTPBasic() |
|
HTTPBearer |
HTTP Bearer Auth | HTTPBearer() |
|
| Middleware | app.add_middleware() |
Add middleware | app.add_middleware(CORSMiddleware) |
CORSMiddleware |
CORS support | CORSMiddleware(allow_origins=["*"]) |
|
@app.middleware("http") |
Custom middleware | @app.middleware("http") |
|
| Routing | APIRouter() |
Create a router | router = APIRouter() |
app.include_router() |
Include a router | app.include_router(router) |
|
router.prefix |
Router prefix | prefix="/api/v1" |
|
router.tags |
Tags for documentation | tags=["items"] |
|
| Events | @app.on_event("startup") |
Startup event | @app.on_event("startup") |
@app.on_event("shutdown") |
Shutdown event | @app.on_event("shutdown") |
|
| WebSocket | @app.websocket() |
WebSocket connection | @app.websocket("/ws") |
websocket.accept() |
Accept connection | await websocket.accept() |
|
websocket.send_text() |
Send text | await websocket.send_text("Hello") |
|
websocket.receive_text() |
Receive text | await websocket.receive_text() |
|
| Background Tasks | BackgroundTasks |
Background tasks | BackgroundTasks |
background_tasks.add_task() |
Add a task | background_tasks.add_task(func) |
|
| Testing | TestClient |
Test client | TestClient(app) |
client.get() |
GET request in tests | client.get("/") |
|
client.post() |
POST request in tests | client.post("/", json={}) |
|
| Configuration | app.openapi_url |
URL for OpenAPI schema | app.openapi_url = "/openapi.json" |
app.docs_url |
URL for Swagger UI | app.docs_url = "/docs" |
|
app.redoc_url |
URL for ReDoc | app.redoc_url = "/redoc" |
|
| Utilities | jsonable_encoder |
Encode to JSON | jsonable_encoder(obj) |
create_response |
Create a response | create_response(content) |
Practical Usage Examples
E‑Commerce API
from fastapi import FastAPI, Depends, HTTPException
from pydantic import BaseModel, Field
from typing import List, Optional
from datetime import datetime
app = FastAPI(title="E‑Commerce API")
class Product(BaseModel):
id: Optional[int] = None
name: str
price: float
description: str
category_id: int
class Category(BaseModel):
id: Optional[int] = None
name: str
@app.get("/products/", response_model=List[Product])
async def get_products(category_id: Optional[int] = None, skip: int = 0, limit: int = 100):
# Logic to retrieve products
return []
@app.post("/products/", response_model=Product)
async def create_product(product: Product):
# Logic to create a product
return product
@app.get("/categories/", response_model=List[Category])
async def get_categories():
# Logic to retrieve categories
return []
Blog API
from datetime import datetime
from typing import List, Optional
from pydantic import BaseModel, Field
class Post(BaseModel):
id: Optional[int] = None
title: str
content: str
author_id: int
created_at: datetime = Field(default_factory=datetime.now)
published: bool = False
class Comment(BaseModel):
id: Optional[int] = None
post_id: int
author_id: int
content: str
created_at: datetime = Field(default_factory=datetime.now)
@app.get("/posts/", response_model=List[Post])
async def get_posts(published: bool = True, skip: int = 0, limit: int = 10):
return []
@app.post("/posts/", response_model=Post)
async def create_post(post: Post):
return post
@app.get("/posts/{post_id}/comments/", response_model=List[Comment])
async def get_post_comments(post_id: int):
return []
Frequently Asked Questions
What are the main differences between FastAPI and Flask?
FastAPI provides automatic data validation, documentation generation, built‑in type support, and asynchronous capabilities. Flask is more minimalistic and requires additional libraries for these features.
Does FastAPI support GraphQL?
Yes, FastAPI can be integrated with GraphQL using libraries such as Strawberry or Graphene.
Can FastAPI be used with Django ORM?
It is possible, but not recommended. Prefer SQLAlchemy, Tortoise ORM, or other async‑compatible ORMs.
How to secure a FastAPI application?
Use HTTPS, proper authentication (JWT, OAuth2), input validation, CORS settings, and keep dependencies up to date.
Does FastAPI include database migration tools?
FastAPI does not include migration tools. Use Alembic for SQLAlchemy or Aerich for Tortoise ORM.
Can FastAPI be used to build full‑stack web applications?
FastAPI is optimized for API development. For full‑stack web apps, frameworks like Django or Flask with templating are more suitable.
FastAPI — a powerful and modern framework that is perfect for building high‑performance APIs. Its ease of use, automatic documentation, and excellent performance make it an ideal choice for projects of any scale.
$
The Future of AI in Mathematics and Everyday Life: How Intelligent Agents Are Already Changing the Game
Experts warned about the risks of fake charity with AI
In Russia, universal AI-agent for robots and industrial processes was developed