← Back to Blog

Building High-Performance APIs with FastAPI

Discover why FastAPI has become the go-to framework for modern API development

Building High-Performance APIs with FastAPI

FastAPI combines the best of modern Python features with excellent performance and developer experience. In this guide, I'll walk you through building a complete REST API from scratch, including authentication, database integration, and deployment considerations. We'll also explore advanced features like dependency injection and background tasks.

Why FastAPI?

FastAPI has quickly become the preferred choice for building APIs in Python. Here's what makes it special:

  • Performance: One of the fastest Python frameworks available
  • Type Hints: Built-in support for Python type hints
  • Automatic Documentation: Interactive API docs with OpenAPI
  • Async Support: Native async/await support
  • Easy to Learn: Intuitive and well-documented

Setting Up Your Project

Let's start by setting up a new FastAPI project with all the necessary dependencies.

# Install FastAPI and dependencies
pip install fastapi uvicorn sqlalchemy pydantic python-multipart

# Create project structure
mkdir fastapi-project
cd fastapi-project
mkdir app
touch app/__init__.py
touch app/main.py

Basic FastAPI Application

Here's a simple FastAPI application to get you started:

from fastapi import FastAPI
from pydantic import BaseModel
from typing import List, Optional

app = FastAPI(title="My API", version="1.0.0")

# Pydantic model for data validation
class Item(BaseModel):
    id: int
    name: str
    description: Optional[str] = None
    price: float
    is_offer: bool = False

# Sample data
items = [
    Item(id=1, name="Laptop", description="High-performance laptop", price=999.99),
    Item(id=2, name="Mouse", description="Wireless mouse", price=29.99)
]

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

@app.get("/items/", response_model=List[Item])
async def get_items():
    return items

@app.get("/items/{item_id}", response_model=Item)
async def get_item(item_id: int):
    for item in items:
        if item.id == item_id:
            return item
    return {"error": "Item not found"}

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

Database Integration with SQLAlchemy

Most real-world applications need a database. Let's integrate SQLAlchemy with FastAPI:

from sqlalchemy import create_engine, Column, Integer, String, Float, Boolean
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker

# Database setup
SQLALCHEMY_DATABASE_URL = "sqlite:///./test.db"
engine = create_engine(SQLALCHEMY_DATABASE_URL)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
Base = declarative_base()

# Database model
class ItemDB(Base):
    __tablename__ = "items"
    
    id = Column(Integer, primary_key=True, index=True)
    name = Column(String, index=True)
    description = Column(String)
    price = Column(Float)
    is_offer = Column(Boolean, default=False)

# Create tables
Base.metadata.create_all(bind=engine)

# Dependency to get database session
def get_db():
    db = SessionLocal()
    try:
        yield db
    finally:
        db.close()

Authentication and Security

Security is crucial for any API. Here's how to implement JWT authentication:

from fastapi import Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from jose import JWTError, jwt
from passlib.context import CryptContext
from datetime import datetime, timedelta

# Security configuration
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 create_access_token(data: dict):
    to_encode = data.copy()
    expire = datetime.utcnow() + timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
    to_encode.update({"exp": expire})
    encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
    return encoded_jwt

def verify_token(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

Advanced Features

FastAPI offers many advanced features that make development easier:

Dependency Injection

from fastapi import Depends

def get_current_user(token: str = Depends(oauth2_scheme)):
    return verify_token(token)

@app.get("/users/me")
async def read_users_me(current_user: str = Depends(get_current_user)):
    return {"username": current_user}

Background Tasks

from fastapi import BackgroundTasks

def send_email_notification(email: str, message: str):
    # Simulate sending email
    print(f"Sending email to {email}: {message}")

@app.post("/send-notification/")
async def send_notification(
    background_tasks: BackgroundTasks,
    email: str,
    message: str
):
    background_tasks.add_task(send_email_notification, email, message)
    return {"message": "Notification sent in background"}

Testing Your API

Testing is essential for maintaining code quality. Here's how to test your FastAPI application:

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": "Hello World"}

def test_create_item():
    item_data = {
        "id": 3,
        "name": "Keyboard",
        "description": "Mechanical keyboard",
        "price": 89.99,
        "is_offer": True
    }
    response = client.post("/items/", json=item_data)
    assert response.status_code == 200
    assert response.json()["name"] == "Keyboard"

Deployment Considerations

When deploying your FastAPI application to production, consider these important factors:

  • ASGI Server: Use Uvicorn or Gunicorn with Uvicorn workers
  • Environment Variables: Store sensitive configuration securely
  • Database Migrations: Use Alembic for database schema changes
  • Monitoring: Implement logging and health checks
  • Load Balancing: Use multiple workers for high traffic

Performance Optimization

FastAPI is already fast, but here are some tips to optimize further:

  • Use async functions for I/O operations
  • Implement proper database indexing
  • Use connection pooling for databases
  • Cache frequently accessed data
  • Monitor and profile your application

Best Practices

Here are some best practices I've learned from building FastAPI applications:

  • Use Pydantic models for request/response validation
  • Implement proper error handling and logging
  • Write comprehensive tests
  • Document your API endpoints
  • Follow RESTful conventions
  • Use environment variables for configuration

Conclusion

FastAPI has transformed how we build APIs in Python. Its combination of performance, developer experience, and modern Python features makes it an excellent choice for both small projects and large-scale applications.

The key to success is starting with a solid foundation and gradually adding complexity as needed. Focus on code quality, testing, and documentation from the beginning.