Back to Backend Development

fastapi-templates

FastAPIPythonAPIBackend DevelopmentREST APIMicroservicesAsynchronousProject Templates
⭐ 36.8kπŸ“„ MITπŸ•’ 2026-06-16Source β†—

Install this skill

npx skills add wshobson/agents

Works across Claude Code, Cursor, Codex, Copilot & Antigravity

The fastapi-templates skill provides a standardized architectural blueprint for constructing scalable, asynchronous Python APIs. It enforces a clean separation of concerns by utilizing a modular directory layout that isolates business logic, data access, and route definitions. This framework prioritizes maintainability through explicit dependency injection patterns and structured Pydantic schema validation. By pre-configuring database sessions, middleware for CORS, and lifespan event management, it allows developers to focus on feature implementation rather than infrastructure boilerplate. The templates adhere to modern asynchronous programming standards, ensuring that IO-bound tasks like database operations do not block the event loop. This approach fosters consistent code quality and predictable project organization across professional backend development environments, facilitating team collaboration and reducing technical debt during the growth phase of a service.

When to Use This Skill

  • β€’Building microservices that require strict structure and async support
  • β€’Starting a new REST API project with industry-standard practices
  • β€’Implementing CRUD-heavy applications that benefit from a repository layer
  • β€’Standardizing database connectivity and error handling across multiple services

How to Invoke This Skill

Example prompts that trigger this skill in Claude Code, Cursor, or Antigravity:

  • β€œGenerate a new FastAPI project structure
  • β€œSet up an async FastAPI repository pattern
  • β€œImplement database dependency injection in FastAPI
  • β€œCreate a modular backend architecture for Python
  • β€œBootstrap a FastAPI project with best practices

Pro Tips

  • πŸ’‘Leverage Pydantic for strict data validation and serialization, ensuring API contract consistency and reducing runtime errors.
  • πŸ’‘Integrate FastAPI's dependency injection system to manage database sessions, authentication, and other shared resources cleanly and testably.
  • πŸ’‘Always include comprehensive unit and integration tests from the outset, using `pytest` and FastAPI's `TestClient` to ensure API reliability.

What this skill does

  • β€’Modular directory structure for API routes, services, and repositories
  • β€’Asynchronous database session management with SQLAlchemy
  • β€’Dependency injection via FastAPI's Depends system
  • β€’Standardized configuration handling using Pydantic Settings
  • β€’Built-in lifespan event management for startup and shutdown tasks
  • β€’Generic base repository pattern for standardized CRUD logic

When not to use it

  • βœ•Rapid prototyping where a single-file script is more efficient
  • βœ•Projects requiring synchronous-only execution models

Example workflow

  1. Initialize the directory structure based on the template
  2. Configure environment variables within the settings module
  3. Define Pydantic schemas for the desired data models
  4. Implement business logic within the service layer
  5. Expose endpoints by registering the routers in the main application
  6. Test database interactions using the established async session dependency

Prerequisites

  • –Python 3.9+
  • –Basic knowledge of FastAPI and Pydantic
  • –Understanding of async/await patterns

Pitfalls & limitations

  • !Over-engineering simple scripts with unnecessary abstraction layers
  • !Mismanaging async database sessions leading to connection leaks
  • !Tight coupling between business logic and route handlers if layers are ignored

FAQ

Why use a repository pattern for simple APIs?
It decouples your business logic from database-specific code, making unit testing easier and allowing you to swap storage engines or ORMs later.
Does this template support migrations?
While the core structure manages sessions, you should pair this with Alembic for handling database schema migrations.
Is this suitable for production?
Yes, it incorporates essential production elements like CORS middleware, environment configuration, and structured async dependency handling.
How does this handle shared dependencies?
The template uses a dedicated dependencies.py module to centralize common objects like database sessions and authenticated user providers.

How it compares

Manually creating these files leads to inconsistent patterns across projects; this skill enforces a repeatable structure that automates setup and architectural design decisions.

Source & trust

⭐ 37k starsπŸ“„ MITπŸ•’ Updated 2026-06-16
πŸ“„ Full skill instructions β€” original source: wshobson/agents
# FastAPI Project Templates

Production-ready FastAPI project structures with async patterns, dependency injection, middleware, and best practices for building high-performance APIs.

## When to Use This Skill

- Starting new FastAPI projects from scratch
- Implementing async REST APIs with Python
- Building high-performance web services and microservices
- Creating async applications with PostgreSQL, MongoDB
- Setting up API projects with proper structure and testing

## Core Concepts

### 1. Project Structure

**Recommended Layout:**

app/
β”œβ”€β”€ api/ # API routes
β”‚ β”œβ”€β”€ v1/
β”‚ β”‚ β”œβ”€β”€ endpoints/
β”‚ β”‚ β”‚ β”œβ”€β”€ users.py
β”‚ β”‚ β”‚ β”œβ”€β”€ auth.py
β”‚ β”‚ β”‚ └── items.py
β”‚ β”‚ └── router.py
β”‚ └── dependencies.py # Shared dependencies
β”œβ”€β”€ core/ # Core configuration
β”‚ β”œβ”€β”€ config.py
β”‚ β”œβ”€β”€ security.py
β”‚ └── database.py
β”œβ”€β”€ models/ # Database models
β”‚ β”œβ”€β”€ user.py
β”‚ └── item.py
β”œβ”€β”€ schemas/ # Pydantic schemas
β”‚ β”œβ”€β”€ user.py
β”‚ └── item.py
β”œβ”€β”€ services/ # Business logic
β”‚ β”œβ”€β”€ user_service.py
β”‚ └── auth_service.py
β”œβ”€β”€ repositories/ # Data access
β”‚ β”œβ”€β”€ user_repository.py
β”‚ └── item_repository.py
└── main.py # Application entry


### 2. Dependency Injection

FastAPI's built-in DI system using Depends:

- Database session management
- Authentication/authorization
- Shared business logic
- Configuration injection

### 3. Async Patterns

Proper async/await usage:

- Async route handlers
- Async database operations
- Async background tasks
- Async middleware

## Implementation Patterns

### Pattern 1: Complete FastAPI Application

# main.py
from fastapi import FastAPI, Depends
from fastapi.middleware.cors import CORSMiddleware
from contextlib import asynccontextmanager

@asynccontextmanager
async def lifespan(app: FastAPI):
"""Application lifespan events."""
# Startup
await database.connect()
yield
# Shutdown
await database.disconnect()

app = FastAPI(
title="API Template",
version="1.0.0",
lifespan=lifespan
)

# CORS middleware
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)

# Include routers
from app.api.v1.router import api_router
app.include_router(api_router, prefix="/api/v1")

# core/config.py
from pydantic_settings import BaseSettings
from functools import lru_cache

class Settings(BaseSettings):
"""Application settings."""
DATABASE_URL: str
SECRET_KEY: str
ACCESS_TOKEN_EXPIRE_MINUTES: int = 30
API_V1_STR: str = "/api/v1"

class Config:
env_file = ".env"

@lru_cache()
def get_settings() -> Settings:
return Settings()

# core/database.py
from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
from app.core.config import get_settings

settings = get_settings()

engine = create_async_engine(
settings.DATABASE_URL,
echo=True,
future=True
)

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

Base = declarative_base()

async def get_db() -> AsyncSession:
"""Dependency for database session."""
async with AsyncSessionLocal() as session:
try:
yield session
await session.commit()
except Exception:
await session.rollback()
raise
finally:
await session.close()


### Pattern 2: CRUD Repository Pattern

# repositories/base_repository.py
from typing import Generic, TypeVar, Type, Optional, List
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select
from pydantic import BaseModel

ModelType = TypeVar("ModelType")
CreateSchemaType = TypeVar("CreateSchemaType", bound=BaseModel)
UpdateSchemaType = TypeVar("UpdateSchemaType", bound=BaseModel)

class BaseRepository(Generic[ModelType, CreateSchemaType, UpdateSchemaType]):
"""Base repository for CRUD operations."""

def __init__(self, model: Type[ModelType]):
self.model = model

async def get(self, db: AsyncSession, id: int) -> Optional[ModelType]:
"""Get by ID."""
result = await db.execute(
select(self.model).where(self.model.id == id)
)
return result.scalars().first()

async def get_multi(
self,
db: AsyncSession,
skip: int = 0,
limit: int = 100
) -> List[ModelType]:
"""Get multiple records."""
result = await db.execute(
select(self.model).offset(skip).limit(limit)
)
return result.scalars().all()

async def create(
self,
db: AsyncSession,
obj_in: CreateSchemaType
) -> ModelType:
"""Create new record."""
db_obj = self.model(**obj_in.dict())
db.add(db_obj)
await db.flush()
await db.refresh(db_obj)
return db_obj

async def update(
self,
db: AsyncSession,
db_obj: ModelType,
obj_in: UpdateSchemaType
) -> ModelType:
"""Update record."""
update_data = obj_in.dict(exclude_unset=True)
for field, value in update_data.items():
setattr(db_obj, field, value)
await db.flush()
await db.refresh(db_obj)
return db_obj

async def delete(self, db: AsyncSession, id: int) -> bool:
"""Delete record."""
obj = await self.get(db, id)
if obj:
await db.delete(obj)
return True
return False

# repositories/user_repository.py
from app.repositories.base_repository import BaseRepository
from app.models.user import User
from app.schemas.user import UserCreate, UserUpdate

class UserRepository(BaseRepository[User, UserCreate, UserUpdate]):
"""User-specific repository."""

async def get_by_email(self, db: AsyncSession, email: str) -> Optional[User]:
"""Get user by email."""
result = await db.execute(
select(User).where(User.email == email)
)
return result.scalars().first()

async def is_active(self, db: AsyncSession, user_id: int) -> bool:
"""Check if user is active."""
user = await self.get(db, user_id)
return user.is_active if user else False

user_repository = UserRepository(User)


### Pattern 3: Service Layer

# services/user_service.py
from typing import Optional
from sqlalchemy.ext.asyncio import AsyncSession
from app.repositories.user_repository import user_repository
from app.schemas.user import UserCreate, UserUpdate, User
from app.core.security import get_password_hash, verify_password

class UserService:
"""Business logic for users."""

def __init__(self):
self.repository = user_repository

async def create_user(
self,
db: AsyncSession,
user_in: UserCreate
) -> User:
"""Create new user with hashed password."""
# Check if email exists
existing = await self.repository.get_by_email(db, user_in.email)
if existing:
raise ValueError("Email already registered")

# Hash password
user_in_dict = user_in.dict()
user_in_dict["hashed_password"] = get_password_hash(user_in_dict.pop("password"))

# Create user
user = await self.repository.create(db, UserCreate(**user_in_dict))
return user

async def authenticate(
self,
db: AsyncSession,
email: str,
password: str
) -> Optional[User]:
"""Authenticate user."""
user = await self.repository.get_by_email(db, email)
if not user:
return None
if not verify_password(password, user.hashed_password):
return None
return user

async def update_user(
self,
db: AsyncSession,
user_id: int,
user_in: UserUpdate
) -> Optional[User]:
"""Update user."""
user = await self.repository.get(db, user_id)
if not user:
return None

if user_in.password:
user_in_dict = user_in.dict(exclude_unset=True)
user_in_dict["hashed_password"] = get_password_hash(
user_in_dict.pop("password")
)
user_in = UserUpdate(**user_in_dict)

return await self.repository.update(db, user, user_in)

user_service = UserService()


### Pattern 4: API Endpoints with Dependencies

# api/v1/endpoints/users.py
from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy.ext.asyncio import AsyncSession
from typing import List

from app.core.database import get_db
from app.schemas.user import User, UserCreate, UserUpdate
from app.services.user_service import user_service
from app.api.dependencies import get_current_user

router = APIRouter()

@router.post("/", response_model=User, status_code=status.HTTP_201_CREATED)
async def create_user(
user_in: UserCreate,
db: AsyncSession = Depends(get_db)
):
"""Create new user."""
try:
user = await user_service.create_user(db, user_in)
return user
except ValueError as e:
raise HTTPException(status_code=400, detail=str(e))

@router.get("/me", response_model=User)
async def read_current_user(
current_user: User = Depends(get_current_user)
):
"""Get current user."""
return current_user

@router.get("/{user_id}", response_model=User)
async def read_user(
user_id: int,
db: AsyncSession = Depends(get_db),
current_user: User = Depends(get_current_user)
):
"""Get user by ID."""
user = await user_service.repository.get(db, user_id)
if not user:
raise HTTPException(status_code=404, detail="User not found")
return user

@router.patch("/{user_id}", response_model=User)
async def update_user(
user_id: int,
user_in: UserUpdate,
db: AsyncSession = Depends(get_db),
current_user: User = Depends(get_current_user)
):
"""Update user."""
if current_user.id != user_id:
raise HTTPException(status_code=403, detail="Not authorized")

user = await user_service.update_user(db, user_id, user_in)
if not user:
raise HTTPException(status_code=404, detail="User not found")
return user

@router.delete("/{user_id}", status_code=status.HTTP_204_NO_CONTENT)
async def delete_user(
user_id: int,
db: AsyncSession = Depends(get_db),
current_user: User = Depends(get_current_user)
):
"""Delete user."""
if current_user.id != user_id:
raise HTTPException(status_code=403, detail="Not authorized")

deleted = await user_service.repository.delete(db, user_id)
if not deleted:
raise HTTPException(status_code=404, detail="User not found")


### Pattern 5: Authentication & Authorization

# core/security.py
from datetime import datetime, timedelta
from typing import Optional
from jose import JWTError, jwt
from passlib.context import CryptContext
from app.core.config import get_settings

settings = get_settings()
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")

ALGORITHM = "HS256"

def create_access_token(data: dict, expires_delta: Optional[timedelta] = None):
"""Create JWT access token."""
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, settings.SECRET_KEY, algorithm=ALGORITHM)
return encoded_jwt

def verify_password(plain_password: str, hashed_password: str) -> bool:
"""Verify password against hash."""
return pwd_context.verify(plain_password, hashed_password)

def get_password_hash(password: str) -> str:
"""Hash password."""
return pwd_context.hash(password)

# api/dependencies.py
from fastapi import Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer
from jose import JWTError, jwt
from sqlalchemy.ext.asyncio import AsyncSession

from app.core.database import get_db
from app.core.security import ALGORITHM
from app.core.config import get_settings
from app.repositories.user_repository import user_repository

oauth2_scheme = OAuth2PasswordBearer(tokenUrl=f"{settings.API_V1_STR}/auth/login")

async def get_current_user(
db: AsyncSession = Depends(get_db),
token: str = Depends(oauth2_scheme)
):
"""Get current authenticated user."""
credentials_exception = HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Could not validate credentials",
headers={"WWW-Authenticate": "Bearer"},
)

try:
payload = jwt.decode(token, settings.SECRET_KEY, algorithms=[ALGORITHM])
user_id: int = payload.get("sub")
if user_id is None:
raise credentials_exception
except JWTError:
raise credentials_exception

user = await user_repository.get(db, user_id)
if user is None:
raise credentials_exception

return user


## Testing

# tests/conftest.py
import pytest
import asyncio
from httpx import AsyncClient
from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession
from sqlalchemy.orm import sessionmaker

from app.main import app
from app.core.database import get_db, Base

TEST_DATABASE_URL = "sqlite+aiosqlite:///:memory:"

@pytest.fixture(scope="session")
def event_loop():
loop = asyncio.get_event_loop_policy().new_event_loop()
yield loop
loop.close()

@pytest.fixture
async def db_session():
engine = create_async_engine(TEST_DATABASE_URL, echo=True)
async with engine.begin() as conn:
await conn.run_sync(Base.metadata.create_all)

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

async with AsyncSessionLocal() as session:
yield session

@pytest.fixture
async def client(db_session):
async def override_get_db():
yield db_session

app.dependency_overrides[get_db] = override_get_db

async with AsyncClient(app=app, base_url="http://test") as client:
yield client

# tests/test_users.py
import pytest

@pytest.mark.asyncio
async def test_create_user(client):
response = await client.post(
"/api/v1/users/",
json={
"email": "[email protected]",
"password": "testpass123",
"name": "Test User"
}
)
assert response.status_code == 201
data = response.json()
assert data["email"] == "[email protected]"
assert "id" in data


## Resources

- **references/fastapi-architecture.md**: Detailed architecture guide
- **references/async-best-practices.md**: Async/await patterns
- **references/testing-strategies.md**: Comprehensive testing guide
- **assets/project-template/**: Complete FastAPI project
- **assets/docker-compose.yml**: Development environment setup

## Best Practices

1. **Async All The Way**: Use async for database, external APIs
2. **Dependency Injection**: Leverage FastAPI's DI system
3. **Repository Pattern**: Separate data access from business logic
4. **Service Layer**: Keep business logic out of routes
5. **Pydantic Schemas**: Strong typing for request/response
6. **Error Handling**: Consistent error responses
7. **Testing**: Test all layers independently

## Common Pitfalls

- **Blocking Code in Async**: Using synchronous database drivers
- **No Service Layer**: Business logic in route handlers
- **Missing Type Hints**: Loses FastAPI's benefits
- **Ignoring Sessions**: Not properly managing database sessions
- **No Testing**: Skipping integration tests
- **Tight Coupling**: Direct database access in routes

How to Use This Skill Unit

Option A: Project-Specific (Recommended)

  1. Click "Download" above
  2. In your project, create the directory: .agent/skills/fastapi-templates/
  3. Save the file as SKILL.md
  4. The agent will automatically discover the skill based on its description.

Option B: Global Installation (All Agents)

Save the file to these locations to make it available across all projects:

  • Claude Code: ~/.claude/skills/wshobson/agents/fastapi-templates/SKILL.md
  • Cursor: ~/.cursor/skills/wshobson/agents/fastapi-templates/SKILL.md
  • Antigravity: ~/.gemini/antigravity/skills/wshobson/agents/fastapi-templates/SKILL.md

πŸš€ Install with CLI:
npx skills add wshobson/agents

Read the Master Guide: Mastering Agent Skills β†’

Recommended Rules

View more rules β†’

Recommended Workflows

View more workflows β†’

Recommended MCP Servers

View more MCP servers β†’

Take It Further

Maximize your productivity with these powerful resources

πŸ“‹

Define Your Standards

Set up coding standards to ensure this workflow produces consistent, high-quality results.

Browse Rules Library
πŸ“–

Master Workflows

Learn how to create custom workflows, use Turbo Mode, and build your automation library.

Complete Guide

How to use this Skill in Claude Code & Cursor

For Claude Code (CLI)

To use this skill in Claude Code, copy the rule content into your project's custom instructions or follow our Add-Skill CLI guide. This ensures Claude follows your standards during every code generation.

For Cursor & Windsurf

For Cursor or Windsurf, individual skills are best used in the "Rules for AI" section. This specific unit helps the agent avoid backend development issues, leading to cleaner, more efficient code.

Why the skill format matters: the standardized Agent Skills format lets your AI agent load detailed instructions only when they are relevant, keeping your prompt clean while improving results.

Source & attribution

This skill is categorized under Backend Development and is published by W. Shobson, maintained in wshobson/agents.

← Browse All Agent Skills
Sponsored AI assistant. Recommendations may be paid.