Introduction to FastAPI

Introduction to FastAPI

What is FastAPI?

FastAPI is a modern, fast web framework for building APIs with Python 3.7+ based on standard Python type hints. Created by Sebastián Ramirez in 2018, FastAPI has quickly become one of the most popular Python web frameworks, especially for building RESTful APIs and microservices. It combines the best features of popular frameworks like Flask, Django, and Starlette while adding powerful new capabilities.

FastAPI is built on top of Starlette (an async web framework) and Pydantic (a data validation library), providing automatic API documentation, type validation, and excellent performance. The framework leverages Python's type hints to automatically generate OpenAPI schemas, validate request data, and provide interactive API documentation.

Why Choose FastAPI?

FastAPI has gained immense popularity for several compelling reasons. Understanding these advantages will help you decide if FastAPI is the right choice for your API projects.

High Performance

FastAPI is one of the fastest Python web frameworks available, rivaling the performance of Node.js and Go applications. It achieves this through async/await support, automatic request validation, and efficient JSON serialization using the orjson library.

Type Safety and Validation

FastAPI uses Python type hints and Pydantic models to automatically validate request and response data. This catches errors early and provides clear error messages. The framework generates JSON schemas automatically from your type hints.

Automatic API Documentation

FastAPI automatically generates interactive API documentation using Swagger UI and ReDoc. This documentation is always up-to-date with your code and allows developers to test endpoints directly from the browser.

Modern Python Features

FastAPI embraces modern Python features like async/await, type hints, and dataclasses. It supports Python 3.7+ and takes advantage of the latest language improvements for better performance and developer experience.

Easy to Learn, Fast to Code

Despite its advanced features, FastAPI has a gentle learning curve. If you know Python basics, you can start building APIs quickly. The framework provides sensible defaults and clear error messages.

Core Concepts

Understanding FastAPI's fundamental concepts is essential for building effective APIs. Let's explore the key building blocks that make FastAPI powerful.

Path Parameters

Path parameters allow you to capture values from the URL path:

from fastapi import FastAPI

app = FastAPI()

@app.get("/users/{user_id}")
async def get_user(user_id: int):
    return {"user_id": user_id, "name": f"User {user_id}"}

@app.get("/files/{file_path:path}")
async def read_file(file_path: str):
    return {"file_path": file_path}

Query Parameters

Query parameters handle optional parameters in the URL:

from typing import Optional
from fastapi import FastAPI

app = FastAPI()

@app.get("/items/")
async def read_items(skip: int = 0, limit: Optional[int] = None):
    return {"skip": skip, "limit": limit}

Request Body

FastAPI uses Pydantic models for request body validation:

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()

class Item(BaseModel):
    name: str
    price: float
    is_offer: bool = None

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

Response Models

You can define response models for automatic validation and documentation:

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()

class Item(BaseModel):
    name: str
    price: float
    tax: float = None

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

Dependency Injection

FastAPI includes a powerful dependency injection system:

from fastapi import Depends, FastAPI

app = FastAPI()

def get_current_user(token: str = Depends(get_token_header)):
    # Validate token and return user
    return User()

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

Getting Started with FastAPI

Let's walk through creating your first FastAPI application and understanding its structure.

Installation and Setup

Installing FastAPI is straightforward using pip:

pip install fastapi uvicorn[standard]

Create a simple FastAPI application:

# main.py
from fastapi import FastAPI

app = FastAPI()

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

@app.get("/items/{item_id}")
async def read_item(item_id: int, q: str = None):
    return {"item_id": item_id, "q": q}

Run the application with Uvicorn:

uvicorn main:app --reload

This starts a development server with automatic reloading.

Project Structure

A typical FastAPI project structure:

fastapi-project/
├── main.py
├── models.py
├── schemas.py
├── crud.py
├── database.py
├── dependencies.py
├── routers/
│   ├── items.py
│   ├── users.py
├── tests/
│   ├── test_main.py
└── requirements.txt

This structure separates concerns and makes the application maintainable.

Creating API Endpoints

FastAPI supports all HTTP methods with automatic request validation:

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

app = FastAPI()

class Item(BaseModel):
    name: str
    price: float
    is_offer: bool = None

items = []

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

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

@app.get("/items/{item_id}", response_model=Item)
async def read_item(item_id: int):
    if item_id >= len(items):
        raise HTTPException(status_code=404, detail="Item not found")
    return items[item_id]

@app.put("/items/{item_id}", response_model=Item)
async def update_item(item_id: int, item: Item):
    if item_id >= len(items):
        raise HTTPException(status_code=404, detail="Item not found")
    items[item_id] = item
    return item

@app.delete("/items/{item_id}")
async def delete_item(item_id: int):
    if item_id >= len(items):
        raise HTTPException(status_code=404, detail="Item not found")
    del items[item_id]
    return {"message": "Item deleted"}

This creates a complete CRUD API with automatic validation and documentation.

Working with Databases

FastAPI works well with various databases. Here's an example with SQLAlchemy:

from sqlalchemy import create_engine, Column, Integer, String
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
from fastapi import Depends, FastAPI
from pydantic import BaseModel

# 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 Item(Base):
    __tablename__ = "items"
    id = Column(Integer, primary_key=True, index=True)
    name = Column(String, index=True)
    price = Column(Integer)

Base.metadata.create_all(bind=engine)

# Pydantic models
class ItemCreate(BaseModel):
    name: str
    price: int

class ItemResponse(BaseModel):
    id: int
    name: str
    price: int

# Dependency
def get_db():
    db = SessionLocal()
    try:
        yield db
    finally:
        db.close()

app = FastAPI()

@app.post("/items/", response_model=ItemResponse)
def create_item(item: ItemCreate, db: Session = Depends(get_db)):
    db_item = Item(name=item.name, price=item.price)
    db.add(db_item)
    db.commit()
    db.refresh(db_item)
    return db_item

Key Features in Detail

Let's dive deeper into some of FastAPI's most powerful features.

Automatic API Documentation

FastAPI automatically generates interactive API documentation. Visit /docs for Swagger UI or /redoc for ReDoc. The documentation includes:

  • All endpoints with their methods
  • Request/response schemas
  • Authentication requirements
  • Example requests

Request Validation and Serialization

FastAPI uses Pydantic for automatic data validation:

from fastapi import FastAPI, HTTPException
from pydantic import BaseModel, validator
from typing import Optional

app = FastAPI()

class User(BaseModel):
    username: str
    email: str
    age: Optional[int] = None
    
    @validator('username')
    def username_must_be_valid(cls, v):
        if len(v) < 3:
            raise ValueError('Username must be at least 3 characters')
        return v
    
    @validator('email')
    def email_must_be_valid(cls, v):
        if '@' not in v:
            raise ValueError('Invalid email')
        return v

@app.post("/users/")
async def create_user(user: User):
    return user

Authentication and Security

FastAPI provides tools for implementing authentication:

from fastapi import Depends, FastAPI, HTTPException, status
from fastapi.security import HTTPBasic, HTTPBasicCredentials
from pydantic import BaseModel

app = FastAPI()
security = HTTPBasic()

class User(BaseModel):
    username: str
    disabled: bool = None

def get_current_user(credentials: HTTPBasicCredentials = Depends(security)):
    # Verify credentials
    if credentials.username != "admin" or credentials.password != "secret":
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Incorrect username or password",
            headers={"WWW-Authenticate": "Basic"},
        )
    return User(username=credentials.username)

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

File Uploads

FastAPI handles file uploads easily:

from fastapi import FastAPI, File, UploadFile
from typing import List

app = FastAPI()

@app.post("/uploadfile/")
async def create_upload_file(file: UploadFile = File(...)):
    return {"filename": file.filename}

@app.post("/uploadfiles/")
async def create_upload_files(files: List[UploadFile] = File(...)):
    return {"filenames": [file.filename for file in files]}

Background Tasks

FastAPI supports background task processing:

from fastapi import BackgroundTasks, FastAPI

app = FastAPI()

def write_notification(email: str, message=""):
    with open("log.txt", mode="w") as email_file:
        content = f"notification for {email}: {message}"
        email_file.write(content)

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

WebSockets

FastAPI supports WebSocket connections for real-time communication:

from fastapi import FastAPI, WebSocket

app = FastAPI()

@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 text was: {data}")

Middleware

FastAPI supports custom middleware for cross-cutting concerns:

from fastapi import Request
from fastapi.responses import JSONResponse

@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

Advanced Features

FastAPI offers advanced features for complex applications.

Routers for Modularization

Use APIRouter to organize your API into modules:

from fastapi import APIRouter, Depends

router = APIRouter()

@router.get("/items/")
async def read_items():
    return [{"name": "Item 1"}, {"name": "Item 2"}]

@router.post("/items/")
async def create_item(item: dict):
    return item

app.include_router(router, prefix="/api/v1", tags=["items"])

Custom Response Classes

Create custom response classes for different content types:

from fastapi import FastAPI
from fastapi.responses import HTMLResponse, JSONResponse

app = FastAPI()

@app.get("/html/", response_class=HTMLResponse)
async def get_html():
    return """
    <html>
        <head>
            <title>Some HTML</title>
        </head>
        <body>
            <h1>Hello World!</h1>
        </body>
    </html>
    """

@app.get("/json/")
async def get_json():
    return JSONResponse(content={"message": "Hello World"})

Testing

FastAPI provides excellent testing support:

from fastapi.testclient import TestClient

client = TestClient(app)

def test_read_main():
    response = client.get("/")
    assert response.status_code == 200
    assert response.json() == {"msg": "Hello World"}

Deployment and Production

FastAPI applications are production-ready with excellent deployment options.

ASGI Server

FastAPI uses ASGI for high-performance async support. Deploy with Uvicorn or other ASGI servers:

uvicorn main:app --host 0.0.0.0 --port 8000 --workers 4

Docker Deployment

FastAPI works great with Docker:

FROM python:3.9

WORKDIR /app

COPY requirements.txt .
RUN pip install -r requirements.txt

COPY . .

CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]

Cloud Deployment

FastAPI can be deployed to:

  • AWS Lambda (with Mangum)
  • Google Cloud Functions
  • Azure Functions
  • Heroku
  • DigitalOcean App Platform

Best Practices

Following these best practices will help you build better FastAPI applications.

Use Type Hints Everywhere

Leverage Python's type system for better validation and documentation:

from typing import List, Optional
from pydantic import BaseModel

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

def calculate_total(items: List[Item]) -> float:
    return sum(item.price + (item.tax or 0) for item in items)

Handle Errors Properly

Use HTTPException for API errors:

from fastapi import HTTPException

@app.get("/items/{item_id}")
async def read_item(item_id: int):
    if item_id not in items:
        raise HTTPException(status_code=404, detail="Item not found")
    return items[item_id]

Use Dependency Injection

Keep your code modular with dependencies:

from fastapi import Depends

def get_database():
    # Database connection logic
    return db

def get_current_user(db = Depends(get_database)):
    # User authentication logic
    return user

@app.get("/protected-route")
async def protected_route(user = Depends(get_current_user)):
    return {"user": user}

Version Your API

Use API versioning for backward compatibility:

from fastapi import APIRouter

v1_router = APIRouter()
v2_router = APIRouter()

app.include_router(v1_router, prefix="/api/v1")
app.include_router(v2_router, prefix="/api/v2")

Implement Proper Logging

Add logging for monitoring and debugging:

import logging
from fastapi import Request

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

@app.middleware("http")
async def log_requests(request: Request, call_next):
    logger.info(f"Request: {request.method} {request.url}")
    response = await call_next(request)
    logger.info(f"Response: {response.status_code}")
    return response

Common Use Cases

FastAPI excels in various API development scenarios.

RESTful APIs

FastAPI is perfect for building RESTful APIs with automatic validation and documentation.

Microservices

Its lightweight nature and async support make it ideal for microservices architecture.

Machine Learning APIs

FastAPI is popular for serving ML models due to its performance and ease of use.

Real-time Applications

With WebSocket support, FastAPI can handle real-time applications like chat systems.

GraphQL APIs

FastAPI can be extended with GraphQL using libraries like Strawberry or Graphene.

Conclusion

FastAPI has revolutionized API development in Python by combining high performance, type safety, and excellent developer experience. Its automatic documentation, validation, and modern Python features make it a standout choice for building robust APIs.

Whether you're building a simple REST API, a complex microservices architecture, or serving machine learning models, FastAPI provides the tools and performance you need. The framework's growing ecosystem, excellent documentation, and supportive community ensure that you have everything needed to succeed.

Start experimenting with FastAPI today, and you'll quickly discover why it's become one of the most popular choices for Python API development. The combination of speed, safety, and ease of use makes FastAPI an excellent investment in your API development skills.

Build with love by Urvil Patel