Skip to main content

FastAPI - Building Lightning-Fast APIs with Python

ยท 8 min read
Shutiye Dev
Software Engineer & Educator

FastAPI is a modern, high-performance web framework for building APIs with Python. It's incredibly fast (comparable to NodeJS and Go), easy to learn, and comes with automatic API documentation. Let's build a complete API to see why developers love it!

๐Ÿš€ Why FastAPI?โ€‹

FastAPI has taken the Python world by storm, and for good reason:

  • โšก Blazingly Fast: One of the fastest Python frameworks available
  • ๐ŸŽฏ Type Safety: Built on Python type hints for automatic validation
  • ๐Ÿ“š Auto Documentation: Interactive API docs (Swagger UI) generated automatically
  • ๐Ÿ”’ Security: Built-in support for OAuth2, JWT tokens, and more
  • ๐ŸŽจ Modern Python: Uses async/await for concurrent operations
  • โš™๏ธ Less Code: Reduce code duplication by up to 40%

Performance Comparisonโ€‹

FastAPI is on par with Node.js and Go:

  • FastAPI: ~20,000-30,000 requests/second
  • Flask: ~3,000-5,000 requests/second
  • Django: ~2,000-4,000 requests/second

๐Ÿ“ฆ Installation & Setupโ€‹

Let's create a virtual environment and install FastAPI:

# Create project directory
mkdir fastapi-blog
cd fastapi-blog

# Create virtual environment
python -m venv venv

# Activate virtual environment
# On Linux/Mac:
source venv/bin/activate
# On Windows:
# venv\Scripts\activate

# Install FastAPI and Uvicorn (ASGI server)
pip install fastapi uvicorn[standard]
pip install sqlalchemy pydantic-settings python-multipart

๐Ÿ—๏ธ Project Structureโ€‹

Create a clean, organized structure:

fastapi-blog/
โ”œโ”€โ”€ app/
โ”‚ โ”œโ”€โ”€ __init__.py
โ”‚ โ”œโ”€โ”€ main.py
โ”‚ โ”œโ”€โ”€ models.py
โ”‚ โ”œโ”€โ”€ schemas.py
โ”‚ โ”œโ”€โ”€ database.py
โ”‚ โ””โ”€โ”€ routers/
โ”‚ โ”œโ”€โ”€ __init__.py
โ”‚ โ””โ”€โ”€ posts.py
โ”œโ”€โ”€ venv/
โ””โ”€โ”€ requirements.txt

๐ŸŽฏ Building a Blog APIโ€‹

Let's build a complete blog API with CRUD operations.

Step 1: Database Configurationโ€‹

Create app/database.py:

from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker

# SQLite database
SQLALCHEMY_DATABASE_URL = "sqlite:///./blog.db"

# Create engine
engine = create_engine(
SQLALCHEMY_DATABASE_URL,
connect_args={"check_same_thread": False}
)

# Create session
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)

# Base class for models
Base = declarative_base()

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

Step 2: Database Modelsโ€‹

Create app/models.py:

from sqlalchemy import Column, Integer, String, Text, DateTime
from sqlalchemy.sql import func
from app.database import Base

class Post(Base):
__tablename__ = "posts"

id = Column(Integer, primary_key=True, index=True)
title = Column(String(200), nullable=False)
content = Column(Text, nullable=False)
author = Column(String(100), nullable=False)
published = Column(Integer, default=1)
created_at = Column(DateTime(timezone=True), server_default=func.now())
updated_at = Column(DateTime(timezone=True), onupdate=func.now())

Step 3: Pydantic Schemasโ€‹

Create app/schemas.py for request/response validation:

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

# Base schema
class PostBase(BaseModel):
title: str = Field(..., min_length=3, max_length=200)
content: str = Field(..., min_length=10)
author: str = Field(..., min_length=2, max_length=100)
published: int = Field(default=1, ge=0, le=1)

# Schema for creating posts
class PostCreate(PostBase):
pass

# Schema for updating posts
class PostUpdate(BaseModel):
title: Optional[str] = Field(None, min_length=3, max_length=200)
content: Optional[str] = Field(None, min_length=10)
author: Optional[str] = None
published: Optional[int] = Field(None, ge=0, le=1)

# Schema for responses
class PostResponse(PostBase):
id: int
created_at: datetime
updated_at: Optional[datetime]

class Config:
from_attributes = True

๐Ÿ’ก Key Concept: Pydantic models automatically validate data and convert types!

Step 4: API Routesโ€‹

Create app/routers/posts.py:

from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy.orm import Session
from typing import List
from app import models, schemas
from app.database import get_db

router = APIRouter(
prefix="/api/posts",
tags=["Posts"]
)

# Get all posts
@router.get("/", response_model=List[schemas.PostResponse])
async def get_posts(
skip: int = 0,
limit: int = 10,
db: Session = Depends(get_db)
):
"""
Retrieve all blog posts with pagination

- **skip**: Number of posts to skip (default: 0)
- **limit**: Maximum number of posts to return (default: 10)
"""
posts = db.query(models.Post).offset(skip).limit(limit).all()
return posts

# Get single post
@router.get("/{post_id}", response_model=schemas.PostResponse)
async def get_post(post_id: int, db: Session = Depends(get_db)):
"""
Retrieve a specific blog post by ID
"""
post = db.query(models.Post).filter(models.Post.id == post_id).first()

if not post:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=f"Post with ID {post_id} not found"
)

return post

# Create post
@router.post("/", response_model=schemas.PostResponse, status_code=status.HTTP_201_CREATED)
async def create_post(
post: schemas.PostCreate,
db: Session = Depends(get_db)
):
"""
Create a new blog post
"""
new_post = models.Post(**post.dict())
db.add(new_post)
db.commit()
db.refresh(new_post)
return new_post

# Update post
@router.put("/{post_id}", response_model=schemas.PostResponse)
async def update_post(
post_id: int,
post_update: schemas.PostUpdate,
db: Session = Depends(get_db)
):
"""
Update an existing blog post
"""
post_query = db.query(models.Post).filter(models.Post.id == post_id)
post = post_query.first()

if not post:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=f"Post with ID {post_id} not found"
)

# Update only provided fields
update_data = post_update.dict(exclude_unset=True)
post_query.update(update_data, synchronize_session=False)
db.commit()
db.refresh(post)

return post

# Delete post
@router.delete("/{post_id}", status_code=status.HTTP_204_NO_CONTENT)
async def delete_post(post_id: int, db: Session = Depends(get_db)):
"""
Delete a blog post
"""
post_query = db.query(models.Post).filter(models.Post.id == post_id)
post = post_query.first()

if not post:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=f"Post with ID {post_id} not found"
)

post_query.delete(synchronize_session=False)
db.commit()

return None

# Search posts
@router.get("/search/", response_model=List[schemas.PostResponse])
async def search_posts(
q: str,
db: Session = Depends(get_db)
):
"""
Search blog posts by title or content
"""
posts = db.query(models.Post).filter(
(models.Post.title.contains(q)) |
(models.Post.content.contains(q))
).all()

return posts

Step 5: Main Applicationโ€‹

Create app/main.py:

from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from app.database import engine
from app import models
from app.routers import posts

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

# Initialize FastAPI
app = FastAPI(
title="Blog API",
description="A modern blog API built with FastAPI",
version="1.0.0",
docs_url="/docs", # Swagger UI
redoc_url="/redoc" # ReDoc
)

# Configure CORS
app.add_middleware(
CORSMiddleware,
allow_origins=["*"], # In production, specify actual origins
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)

# Include routers
app.include_router(posts.router)

# Root endpoint
@app.get("/")
async def root():
return {
"message": "Welcome to Blog API",
"docs": "/docs",
"version": "1.0.0"
}

# Health check
@app.get("/health")
async def health_check():
return {"status": "healthy"}

๐Ÿš€ Running the Applicationโ€‹

Start your FastAPI server:

uvicorn app.main:app --reload

Your API is now running at:

๐Ÿงช Testing the APIโ€‹

Using curlโ€‹

Create a post:

curl -X POST "http://localhost:8000/api/posts/" \
-H "Content-Type: application/json" \
-d '{
"title": "Getting Started with FastAPI",
"content": "FastAPI is an amazing framework for building APIs quickly!",
"author": "Shutiye Dev",
"published": 1
}'

Get all posts:

curl "http://localhost:8000/api/posts/"

Get a specific post:

curl "http://localhost:8000/api/posts/1"

Update a post:

curl -X PUT "http://localhost:8000/api/posts/1" \
-H "Content-Type: application/json" \
-d '{
"title": "FastAPI Advanced Guide",
"content": "Updated content about FastAPI features"
}'

Search posts:

curl "http://localhost:8000/api/posts/search/?q=FastAPI"

Using Python Requestsโ€‹

import requests

# Create a post
response = requests.post(
"http://localhost:8000/api/posts/",
json={
"title": "Python FastAPI Tutorial",
"content": "Learn FastAPI step by step",
"author": "Dev Team"
}
)
print(response.json())

# Get all posts
response = requests.get("http://localhost:8000/api/posts/")
print(response.json())

โœจ Advanced Featuresโ€‹

1. Async Database Operationsโ€‹

For better performance, use async database operations:

from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession
from sqlalchemy.orm import sessionmaker

DATABASE_URL = "sqlite+aiosqlite:///./blog.db"

engine = create_async_engine(DATABASE_URL)
AsyncSessionLocal = sessionmaker(engine, class_=AsyncSession, expire_on_commit=False)

async def get_async_db():
async with AsyncSessionLocal() as session:
yield session

2. Authentication with JWTโ€‹

from fastapi.security import OAuth2PasswordBearer
from jose import JWTError, jwt

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")

@router.get("/protected")
async def protected_route(token: str = Depends(oauth2_scheme)):
# Verify token
return {"message": "This is protected!"}

3. File Uploadโ€‹

from fastapi import File, UploadFile

@router.post("/upload")
async def upload_file(file: UploadFile = File(...)):
contents = await file.read()
# Process file
return {"filename": file.filename, "size": len(contents)}

4. Background Tasksโ€‹

from fastapi import BackgroundTasks

def send_email(email: str, message: str):
# Send email logic
print(f"Sending email to {email}: {message}")

@router.post("/send-notification")
async def send_notification(
email: str,
background_tasks: BackgroundTasks
):
background_tasks.add_task(send_email, email, "Post created!")
return {"message": "Notification will be sent"}

๐ŸŽฏ Best Practicesโ€‹

1. Use Dependency Injectionโ€‹

from fastapi import Depends

def common_parameters(skip: int = 0, limit: int = 100):
return {"skip": skip, "limit": limit}

@router.get("/items/")
async def read_items(commons: dict = Depends(common_parameters)):
return commons

2. Implement Proper Error Handlingโ€‹

from fastapi import HTTPException

@router.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",
headers={"X-Error": "Custom header"}
)
return items[item_id]

3. Use Environment Variablesโ€‹

from pydantic_settings import BaseSettings

class Settings(BaseSettings):
database_url: str
secret_key: str

class Config:
env_file = ".env"

settings = Settings()

4. Add Response Modelsโ€‹

@router.get("/users/", response_model=List[UserResponse])
async def get_users():
return users # Automatically validates and serializes

๐Ÿ“Š Performance Tipsโ€‹

  1. Use async/await for I/O operations
  2. Implement caching with Redis
  3. Add database connection pooling
  4. Use pagination for large datasets
  5. Optimize database queries with proper indexing
  6. Compress responses with gzip

๐Ÿš€ Deploymentโ€‹

Using Dockerโ€‹

Create Dockerfile:

FROM python:3.11-slim

WORKDIR /app

COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY ./app ./app

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

Build and run:

docker build -t fastapi-blog .
docker run -p 8000:8000 fastapi-blog

Using Uvicorn in Productionโ€‹

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

๐ŸŽ“ Key Takeawaysโ€‹

  • FastAPI is fast: Performance comparable to Node.js and Go
  • Type hints: Automatic validation and documentation
  • Modern Python: Async/await support out of the box
  • Great DX: Amazing developer experience with auto-completion
  • Production-ready: Built-in features for security and performance

๐Ÿ“š What's Next?โ€‹

  • Add authentication with OAuth2 and JWT
  • Implement WebSocket for real-time features
  • Add GraphQL support
  • Integrate with Celery for background tasks
  • Set up monitoring with Prometheus
  • Deploy to AWS, Azure, or Google Cloud

FastAPI is perfect for building modern, high-performance APIs. Its simplicity and speed make it an excellent choice for both beginners and experienced developers!


Want to learn more about FastAPI? Check out the official documentation or explore our Python tutorials.