fastapi-templates

FastAPI Project Templates

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "fastapi-templates" with this command: npx skills add hermeticormus/libreuiux-claude-code/hermeticormus-libreuiux-claude-code-fastapi-templates

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

  1. Dependency Injection

FastAPI's built-in DI system using Depends :

  • Database session management

  • Authentication/authorization

  • Shared business logic

  • Configuration injection

  1. 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": "test@example.com", "password": "testpass123", "name": "Test User" } ) assert response.status_code == 201 data = response.json() assert data["email"] == "test@example.com" 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

  • Async All The Way: Use async for database, external APIs

  • Dependency Injection: Leverage FastAPI's DI system

  • Repository Pattern: Separate data access from business logic

  • Service Layer: Keep business logic out of routes

  • Pydantic Schemas: Strong typing for request/response

  • Error Handling: Consistent error responses

  • 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

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Coding

premium-saas-design

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

design-principles

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

debugging-strategies

No summary provided by upstream source.

Repository SourceNeeds Review