⚡ FastAPI with Uvicorn: Building High-Performance Python APIs
Complete guide to building fast, modern APIs with FastAPI and Uvicorn. Learn how to create production-ready Python web services with automatic documentation and async support.
FastAPI with Uvicorn: Building High-Performance Python APIs
FastAPI is a modern, fast web framework for building APIs with Python. Combined with Uvicorn, an ASGI web server, you can create production-ready applications with minimal boilerplate and excellent performance.
What is FastAPI?
FastAPI is a web framework for building APIs with Python that:
- Is fast - Performance comparable to Node.js and Go
- Requires minimal code - Reduces bugs and development time
- Is standards-based - Built on OpenAPI and JSON Schema
- Provides automatic docs - Swagger UI and ReDoc included
- Supports async - Native async/await support
What is Uvicorn?
Uvicorn is an ASGI web server implementation for Python that:
- Handles concurrent requests - Efficiently manages multiple connections
- Supports async frameworks - Works seamlessly with async Python code
- Is lightweight - Minimal overhead for production deployments
- Is production-ready - Used by major Python web frameworks
Prerequisites
- Python 3.7+
- pip (Python package manager)
- Basic understanding of REST APIs
- Familiarity with async/await in Python
Installation
# Create a virtual environment python -m venv venv # Activate the virtual environment # On macOS/Linux: source venv/bin/activate # On Windows: venv\Scripts\activate # Install FastAPI and Uvicorn pip install fastapi uvicorn[standard] The [standard] extras installs additional dependencies for HTTP/2 and SSL support.
Your First FastAPI Application
Create a file called main.py:
from fastapi import FastAPI app = FastAPI() @app.get("/") def read_root(): return {"message": "Hello, FastAPI!"} @app.get("/items/{item_id}") def read_item(item_id: int, q: str = None): return {"item_id": item_id, "query": q} Run the server with Uvicorn:
uvicorn main:app --reload Visit http://localhost:8000 in your browser. Your API is live!
Automatic Documentation
FastAPI automatically generates interactive documentation:
- Swagger UI: http://localhost:8000/docs
- ReDoc: http://localhost:8000/redoc
Core Concepts
Path Parameters
Path parameters are part of the URL path:
@app.get("/users/{user_id}") async def get_user(user_id: int): return {"user_id": user_id} Access: GET /users/123
Query Parameters
Query parameters come after the ? in the URL:
@app.get("/search/") async def search(q: str, skip: int = 0, limit: int = 10): return {"query": q, "skip": skip, "limit": limit} Access: GET /search/?q=python&skip=0&limit=20
Request Bodies
Accept JSON request bodies:
from pydantic import BaseModel class Item(BaseModel): name: str description: str = None price: float tax: float = None @app.post("/items/") async def create_item(item: Item): return item Request:
POST /items/ { "name": "Laptop", "description": "A powerful laptop", "price": 1299.99, "tax": 130.00 } Response Models
Define expected response format:
from typing import List class Item(BaseModel): id: int name: str price: float @app.get("/items/", response_model=List[Item]) async def list_items(): return [ {"id": 1, "name": "Item 1", "price": 10.0}, {"id": 2, "name": "Item 2", "price": 20.0} ] HTTP Methods
Support different HTTP methods:
# GET - retrieve data @app.get("/items/") async def list_items(): pass # POST - create data @app.post("/items/") async def create_item(item: Item): pass # PUT - update entire resource @app.put("/items/{item_id}") async def update_item(item_id: int, item: Item): pass # PATCH - partial update @app.patch("/items/{item_id}") async def patch_item(item_id: int, item: ItemUpdate): pass # DELETE - remove data @app.delete("/items/{item_id}") async def delete_item(item_id: int): pass Async and Performance
FastAPI supports native async/await for high-performance operations:
import asyncio import httpx @app.get("/async-example/") async def async_operation(): # Non-blocking I/O operations async with httpx.AsyncClient() as client: response = await client.get("https://api.example.com/data") return response.json() Benefits:
- Handle thousands of concurrent requests
- Efficient resource utilization
- Non-blocking I/O operations
Data Validation
Pydantic provides automatic validation:
from pydantic import BaseModel, Field from typing import Optional class User(BaseModel): id: int name: str = Field(..., min_length=1, max_length=100) email: str age: Optional[int] = Field(None, ge=0, le=150) class Config: # Example data in documentation schema_extra = { "example": { "id": 1, "name": "John Doe", "email": "john@example.com", "age": 30 } } Validation features:
- Type checking
- String length constraints
- Number ranges
- Regex patterns
- Custom validators
Error Handling
Return proper HTTP status codes and error messages:
from fastapi import HTTPException, status @app.get("/items/{item_id}") async def read_item(item_id: int): if item_id not in items_db: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="Item not found", headers={"X-Error": "Item lookup failed"} ) return items_db[item_id] Common status codes:
200- OK201- Created400- Bad Request401- Unauthorized403- Forbidden404- Not Found500- Internal Server Error
Middleware
Add middleware to process requests/responses:
from fastapi.middleware.cors import CORSMiddleware from fastapi.middleware.trustedhost import TrustedHostMiddleware # CORS middleware app.add_middleware( CORSMiddleware, allow_origins=["https://example.com"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) # Trusted host middleware app.add_middleware( TrustedHostMiddleware, allowed_hosts=["example.com", "*.example.com"] ) # Custom middleware @app.middleware("http") async def add_process_time_header(request, call_next): import time 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 Complete Example: Todo API
from fastapi import FastAPI, HTTPException, status from pydantic import BaseModel from typing import List, Optional import uuid app = FastAPI(title="Todo API", version="1.0.0") class TodoCreate(BaseModel): title: str description: Optional[str] = None completed: bool = False class Todo(TodoCreate): id: str # In-memory database (use a real database in production) todos_db: dict = {} @app.get("/todos/", response_model=List[Todo]) async def list_todos(): """Get all todos""" return list(todos_db.values()) @app.post("/todos/", response_model=Todo, status_code=status.HTTP_201_CREATED) async def create_todo(todo: TodoCreate): """Create a new todo""" todo_id = str(uuid.uuid4()) new_todo = Todo(id=todo_id, **todo.dict()) todos_db[todo_id] = new_todo return new_todo @app.get("/todos/{todo_id}", response_model=Todo) async def get_todo(todo_id: str): """Get a specific todo""" if todo_id not in todos_db: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="Todo not found" ) return todos_db[todo_id] @app.put("/todos/{todo_id}", response_model=Todo) async def update_todo(todo_id: str, todo: TodoCreate): """Update a todo""" if todo_id not in todos_db: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="Todo not found" ) updated_todo = Todo(id=todo_id, **todo.dict()) todos_db[todo_id] = updated_todo return updated_todo @app.delete("/todos/{todo_id}", status_code=status.HTTP_204_NO_CONTENT) async def delete_todo(todo_id: str): """Delete a todo""" if todo_id not in todos_db: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="Todo not found" ) del todos_db[todo_id] Deployment with Uvicorn
Development Mode
uvicorn main:app --reload Production Mode
# Single worker uvicorn main:app --host 0.0.0.0 --port 8000 # Multiple workers (use gunicorn) pip install gunicorn gunicorn -w 4 -k uvicorn.workers.UvicornWorker main:app Production settings:
--host 0.0.0.0- Listen on all network interfaces--port 8000- Run on port 8000--workers 4- Run multiple worker processes--log-level info- Set logging level
Environment Variables
# Create .env file DATABASE_URL=postgresql://user:password@localhost/dbname SECRET_KEY=your-secret-key # Load in Python from dotenv import load_dotenv import os load_dotenv() database_url = os.getenv("DATABASE_URL") Best Practices
1. Use Type Hints
Type hints improve code quality and enable automatic validation:
from typing import Optional, List @app.get("/items/") async def list_items(skip: int = 0, limit: int = 10) -> List[Item]: pass 2. Organize with Routers
Split endpoints into modular routers:
from fastapi import APIRouter router = APIRouter(prefix="/items", tags=["items"]) @router.get("/") async def list_items(): pass @router.post("/") async def create_item(item: Item): pass # Include in main app app.include_router(router) 3. Add API Documentation
app = FastAPI( title="My API", description="An awesome API", version="1.0.0", openapi_url="/api/v1/openapi.json", docs_url="/api/v1/docs", redoc_url="/api/v1/redoc" ) 4. Handle Dependencies
from fastapi import Depends async def get_query_param(q: Optional[str] = None): return q @app.get("/items/") async def read_items(query: str = Depends(get_query_param)): pass 5. Add Security
from fastapi.security import HTTPBearer, HTTPAuthCredentials security = HTTPBearer() @app.get("/secure/") async def secure_endpoint(credentials: HTTPAuthCredentials = Depends(security)): return {"token": credentials.credentials} Testing FastAPI Apps
from fastapi.testclient import TestClient client = TestClient(app) def test_read_root(): response = client.get("/") assert response.status_code == 200 assert response.json() == {"message": "Hello, FastAPI!"} def test_create_item(): response = client.post("/items/", json={"name": "Test", "price": 10.0}) assert response.status_code == 201 Common Integrations
Database Integration
pip install sqlalchemy Authentication
pip install python-jose pip install passlib pip install python-multipart API Documentation
FastAPI automatically generates docs, but you can customize:
- Swagger UI at
/docs - ReDoc at
/redoc - OpenAPI JSON at
/openapi.json
Performance Tips
- Use async endpoints - For I/O-heavy operations
- Run multiple workers - Use gunicorn with Uvicorn workers
- Cache responses - Reduce database queries
- Use CDN - Cache static assets
- Monitor with tools - Track performance metrics
Resources
Conclusion
FastAPI with Uvicorn provides a powerful foundation for building modern, high-performance Python APIs. With automatic documentation, type validation, and async support, you can create robust applications quickly.
Start with simple endpoints, leverage FastAPI’s validation system, and scale with async operations for maximum performance.
Happy API building! 🚀