Skip to content

akhil2308/fastapi-large-app-template

Repository files navigation

FastAPI Large Application Template πŸš€

FastAPI PostgreSQL Redis

A production-ready FastAPI template designed for building secure, scalable APIs with modern best practices baked in. This template provides a robust foundation for enterprise-grade applications, featuring essential security measures, performance optimizations, and maintainable architecture patterns out of the box.

Features ✨

  • JWT Authentication with refresh tokens πŸ”’
  • Custom Rate Limiting per user/service ⏱️
  • Unified Logging (UVICORN + GUNICORN) πŸ“
  • Redis Connection Pooling (Async) with fail-open strategy 🧠
  • PostgreSQL Connection Pooling (Async) with health checks 🐘
  • Standardized API Responses πŸ“¦
  • Alembic for Database Migrations πŸ—„οΈ
  • Modern Package Management with uv ⚑
  • Production-Ready Error Handling πŸ›‘οΈ
  • Docker + Gunicorn + Uvicorn Stack 🐳⚑
  • OpenTelemetry Observability β€” metrics, traces, Golden Signals dashboards (Prometheus + Grafana + Tempo) πŸ“Š

Observability πŸ“Š

This template includes a production-grade OpenTelemetry observability setup designed for real-world systems:

  • Automatic FastAPI instrumentation
  • Distributed tracing with Tempo
  • Golden Signals dashboards in Grafana
  • Prometheus metrics via OpenTelemetry
  • Custom application & rate-limiter visibility

Observability Stack

OpenTelemetry Prometheus Grafana Grafana Tempo

πŸ‘‰ All observability details live here: πŸ“„ docs/OBSERVABILITY.md

Tech Stack πŸ› οΈ

Component Technology
Framework FastAPI 0.111+
Database PostgreSQL 14+
Cache Redis 6+
ORM SQLAlchemy 2.0
Migrations Alembic
Authentication JWT (OAuth2 Password Bearer)
Rate Limiting Redis-backed Custom Implementation
Package Manager uv (fast Python installer)
Containerization Docker
Observability OpenTelemetry

Project Structure 🌳

The repository follows a modular, domain-oriented structure designed for large, production-grade FastAPI applications:

  • app/ β€” Core FastAPI application

    • Domain modules (user, todo, health)
    • Core configuration, database, logging (core/)
    • Shared utilities: auth dependency, rate limiter (utils/)
    • OpenTelemetry observability setup (observability/)
    • Alembic migrations
    • Application entry point (main.py)
  • docker/observability/ β€” Local observability stack

    • OpenTelemetry Collector
    • Prometheus
    • Grafana (pre-provisioned dashboards & datasources)
    • Tempo (distributed tracing)
  • docs/ β€” Documentation & assets

    • Observability guide and dashboards
    • Architecture diagrams and screenshots
  • tests/ β€” Automated tests

  • Root files β€” Dockerfile, Makefile, run.sh, pyproject.toml, uv.lock, etc.


Key Implementations πŸ”‘

Database Pooling Configuration

PostgreSQL (SQLAlchemy 2.0 + asyncpg):

from sqlalchemy.ext.asyncio import create_async_engine, async_sessionmaker

# Async PostgreSQL connection pool
engine = create_async_engine(
    "postgresql+asyncpg://user:pass@host:port/dbname",
    pool_size=20,          # Persistent connection pool size
    max_overflow=10,       # Temporary connections beyond pool_size
    pool_recycle=300,      # Recycle connections every 300s
    pool_pre_ping=True,    # Validate connections before use
    future=True            # Enable SQLAlchemy 2.0 behavior
)

# Async session factory configuration
AsyncSessionLocal = async_sessionmaker(
    bind=engine,
    expire_on_commit=False,  # Prevent attribute expiration on commit
    autoflush=False,         # Manual flush control
    class_=AsyncSession      # Use SQLAlchemy's async session class
)

Key Features:

  • πŸš€ Full Async Support: Non-blocking database operations via asyncpg
  • πŸ”„ Connection Recycling: Prevents stale connections in long-running applications
  • 🩺 Connection Validation: Pre-ping checks verify connection health
  • πŸ“ˆ Optimized Pooling: Balances memory usage and concurrent requests
  • ⚑ SQLAlchemy 2.0: Future-proof API with explicit transaction control

Redis Connection Pool:

redis = await Redis(
    host="redis.prod.internal",
    port=6379,
    db=0,
    password="securepassword",
    socket_connect_timeout=5,    # 5s connection timeout
    socket_keepalive=True,       # Maintain TCP keepalive
    retry_on_timeout=True,       # Auto-retry failed operations
    max_connections=100,         # Max pool size
    health_check_interval=30     # Validate connections every 30s
)
  • Enterprise Features: TLS support, cluster mode ready
  • Resiliency: Automatic retries and health checks

πŸ”’ Secure Endpoint Example

Protected Todo Creation:

@router.post("/")
async def create_todo(
    body: TodoCreate,
    current_user: User = Depends(get_current_user),
    db: AsyncSession = Depends(get_db)
):
    """
    Implements:
    - JWT Authentication
    - User-based Rate Limiting
    - Structured Error Handling
    - Audit Logging
    """
    try:
        # Rate limit check
        await user_rate_limiter(current_user.user_id, "todo_write")

        # Business logic
        data = await create_todo_service(current_user.user_id, body, db)

        # Standardized success response
        return {
            "status": "success",
            "message": "Todo created",
            "data": data
        }

    except HTTPException as e:
        # Preserve existing HTTP exceptions
        raise
    except Exception as e:
        # Log full error context
        logger.error(f"Todo creation failed: {str(e)}", exc_info=True)
        # Return standardized error format
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail="Internal server error"
        )

⏱️ Custom Rate Limiting

Implementation:

async def user_rate_limiter(
    user_id: str,
    service: str,
    times: int = 5,
    seconds: int = 60
):
    """
    Redis-backed rate limiter using LUA scripts for atomic operations
    """
    key = f"rl:user:{user_id}:{service}"
    try:
        pexpire = await FastAPILimiter.redis.evalsha(
            FastAPILimiter.lua_sha, 1,
            key,
            str(times),
            str(seconds * 1000)  # Convert to milliseconds
        )
        if pexpire != 0:
            raise HTTPException(
                status_code=429,
                detail=f"Try again in {ceil(pexpire/1000)} seconds"
            )
    except Exception as e:
        logger.error(f"Rate limit check failed: {str(e)}")
        # Fail-open during Redis outages

Features:

  • βœ… User+service specific limits
  • βœ… Atomic Redis operations via LUA scripts
  • βœ… Fail-open circuit breaker pattern
  • βœ… Millisecond precision timeouts
  • βœ… Automatic retry-after calculation

πŸ“ Unified Logging System

Configuration:

logging_config = {
    "version": 1,
    "formatters": {
        "standard": {
            "format": "[{asctime}] [{process}] [{levelname}] {module}.{funcName}:{lineno} - {message}",
            "datefmt": "%Y-%m-%d %H:%M:%S %z",
            "style": "{"
        }
    },
    "handlers": {
        "console": {
            "class": "logging.StreamHandler",
            "formatter": "standard",
            "stream": "ext://sys.stdout"
        }
    },
    "loggers": {
        "": {"level": "DEBUG", "handlers": ["console"], "propagate": False},
        "uvicorn": {"level": "INFO", "propagate": False},
        "uvicorn.access": {"level": "INFO", "propagate": False},
        "uvicorn.error": {"level": "INFO", "propagate": False}
    }
}

Log Example:

[2024-05-20 14:30:45 +0000] [1234] [INFO] todo.routers.create_todo:52 - Created todo ID:42

Features:

  • πŸ“Œ Consistent timestamp with timezone
  • πŸ“Œ Process ID tracking
  • πŸ“Œ Module/function/line number context
  • πŸ“Œ Uvicorn log unification
  • πŸ“Œ Production-ready INFO level defaults

πŸ“¦ Standardized API Response

Success Response:

{
  "status": "success",
  "message": "Todo created successfully",
  "data": {
    "id": 42,
    "task": "Implement rate limiting"
  }
}

Error Response:

{
  "status": "error",
  "message": "Validation Failed",
  "errors": [
    {
      "type": "missing",
      "loc": ["body", "task"],
      "msg": "Field required",
      "input": {}
    }
  ]
}

Implementation:

@app.exception_handler(RequestValidationError)
async def validation_handler(request: Request, exc: RequestValidationError):
    errors = [
        {"type": e.get("type"), "loc": e.get("loc"), "msg": e.get("msg"), "input": e.get("input")}
        for e in exc.errors()
    ]
    return JSONResponse(
        status_code=422,
        content={
            "status": "error",
            "message": "Validation Failed",
            "errors": errors,
        }
    )

@app.exception_handler(HTTPException)
async def http_handler(request: Request, exc: HTTPException):
    return JSONResponse(
        status_code=exc.status_code,
        content={
            "status": "error",
            "message": exc.detail,
            "errors": getattr(exc, "errors", None),
        }
    )

Features:

  • βœ… RFC-compliant error formats
  • βœ… Automatic validation error parsing
  • βœ… Consistent error code mapping
  • βœ… Detailed error context preservation

Getting Started

Prerequisites

  • Python 3.11+
  • PostgreSQL 14+
  • Redis 6+
  • Docker (optional)

Installation

git clone https://github.com/akhil2308/fastapi-large-app-template.git
cd fastapi-large-app-template

# Install uv (if not already installed)
pip install uv

# Sync dependencies and create virtual environment
# This installs all packages defined in pyproject.toml
uv sync --all-extras

# Install Git Hooks
# This ensures code quality checks run automatically on commit
uv run pre-commit install

Using Makefile

This project includes a Makefile with convenient commands. Run make help to see all available targets.

# Show all available commands
make help

# Install dependencies and setup git hooks
make install

# Development server
make dev

# Production server
make prod

# Run tests
make test

# Database migrations
make migrate
make migrate-create MSG="your migration message"

# Code quality
make check-env
make lint
make format
make typecheck

# Full CI pipeline
make ci

# Clean generated files
make clean

Configuration

Set environment variables

cp .env.example .env
# Fill in DB, Redis, JWT, and other values

Alembic Commands (Migrations)

Generate new migration:

uv run alembic -c app/alembic.ini revision --autogenerate -m "message"

Apply migrations:

uv run alembic -c app/alembic.ini upgrade head

Running

Development:

uv run uvicorn app.main:app --reload --host 0.0.0.0 --port 8000

Production:

./run.sh  # Starts Gunicorn with Uvicorn workers

Code Standards & Quality πŸ›‘οΈ

This project enforces code quality using Ruff (linter/formatter) and Mypy (static type checker).

Manual Checks

1. Linting & Formatting (Ruff)

# See what code Ruff wants to fix (Dry Run)
uv run ruff check .

# Actually fix the code (Auto-formatting & Import sorting)
uv run ruff check . --fix
uv run ruff format .

2. Static Type Checking (Mypy)

# Check for type errors
uv run mypy .

Note: πŸ”’ A pre-commit hook is configured. When you attempt to git commit, these checks will automatically run to ensure no bad code is pushed to the repository.


API Documentation πŸ“š

Access interactive docs after starting server:

  • Swagger UI: http://localhost:8000/docs
  • ReDoc: http://localhost:8000/redoc
  • OpenAPI: http://localhost:8000/openapi.json

Contributing

See CONTRIBUTORS.txt for contribution guidelines and code of conduct.

License

This project is licensed under the MIT License - see LICENSE for details.

About

πŸš€ Production-grade FastAPI template β€’ JWT auth β€’ Rate limiting β€’ Async PostgreSQL & Redis β€’ OpenTelemetry observability (Prometheus, Grafana, Tempo) β€’ Alembic β€’ Docker β€’ Gunicorn + Uvicorn β€’ Async Ready β€’ RFC-Compliant API Responses β€’ Enterprise Security Patterns

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages