Skip to content

ulugbek101/fastapi-starter-pack

Repository files navigation

FastAPI Production Template

Batteries-included FastAPI starter with PostgreSQL, Redis, async SQLAlchemy, Alembic, structured logging, rate limiting, and full Docker setup. No features pre-built — just a clean, production-ready foundation to build on.


Stack

Layer Technology
Framework FastAPI 0.115 + Python 3.12
Database PostgreSQL 16 via asyncpg + SQLAlchemy 2 async
Migrations Alembic (async engine, autogenerate)
Cache / Rate limit Redis 7 via redis-py async
Logging structlog (JSON in prod, colored in dev)
Testing pytest-asyncio + httpx ASGI client
CI GitHub Actions

Project Structure

fastapi-template/
├── app/
│   ├── api/
│   │   ├── deps.py                  # Shared FastAPI dependencies (DBDep)
│   │   └── v1/
│   │       ├── router.py            # Aggregates all v1 route modules
│   │       └── routes/
│   │           └── health.py        # GET /health
│   ├── core/
│   │   ├── settings.py              # All config via Pydantic BaseSettings + .env
│   │   ├── logging.py               # structlog setup, JSON or console renderer
│   │   └── exceptions.py            # Domain exceptions + FastAPI exception handlers
│   ├── db/
│   │   ├── base.py                  # Imports Base + all models (required for Alembic autogenerate)
│   │   └── session.py               # Async engine, sessionmaker, get_db dependency
│   ├── models/
│   │   └── base.py                  # Declarative base with UUID PK + timestamps
│   ├── schemas/
│   │   ├── base.py                  # BaseSchema (from_attributes=True) + TimestampedSchema
│   │   └── common.py                # HealthResponse, MessageResponse
│   ├── services/                    # Business logic layer (empty — add your services here)
│   ├── repositories/
│   │   └── base.py                  # Generic CRUD: get, get_multi, create, update, delete
│   ├── middleware/
│   │   ├── logging.py               # Per-request structured logging + X-Request-ID header
│   │   └── rate_limit.py            # Redis token-bucket rate limiter per IP
│   ├── utils/
│   │   └── pagination.py            # PaginationMeta dataclass with offset/pages helpers
│   └── main.py                      # App factory, lifespan (Redis connect), middleware stack
├── alembic/
│   ├── env.py                       # Async Alembic runner; reads DATABASE_URL from settings
│   └── versions/                    # Auto-generated migration files go here
├── tests/
│   ├── conftest.py                  # Async fixtures: engine, DB session, ASGI test client
│   └── api/v1/
│       └── test_health.py           # Health endpoint smoke test
├── .github/workflows/ci.yml         # GitHub Actions: migrate + test on push/PR
├── .env.example                     # Template for .env — all variables documented
├── docker-compose.yml               # Dev stack (api + db + redis) with hot reload
├── docker-compose.prod.yml          # Prod overrides (no volume mounts, more workers)
├── Dockerfile                       # Multi-stage build (builder → runtime, non-root user)
├── alembic.ini
├── requirements.txt
└── pyproject.toml                   # pytest, coverage, ruff, mypy config

Before You Run — Required Setup

1. Copy and fill .env

cp .env.example .env

Open .env and set the required values:

Variable How to set it
POSTGRES_USER Your choice, e.g. appuser
POSTGRES_PASSWORD A strong password
POSTGRES_DB Your choice, e.g. myapp

Everything else has a working default for local development.

2. Add your domain to CORS_ORIGINS (if a frontend exists)

CORS_ORIGINS=http://localhost:3000,https://yourfrontend.com

3. (Optional) Configure email

Set EMAILS_ENABLED=true and fill the SMTP_* variables. Leave all blank to disable — nothing will break.

4. (Optional) Configure S3/MinIO

Fill S3_BUCKET, AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY. Set S3_ENDPOINT_URL for MinIO or Cloudflare R2. Leave blank to disable.


Running Locally with Docker

# Start all services (api + postgres + redis)
docker compose up --build

# In a separate terminal — create initial migration (first time only)
docker compose exec api alembic revision --autogenerate -m "initial"
docker compose exec api alembic upgrade head

# API is available at:
# http://localhost:8000
# http://localhost:8000/docs  (Swagger — disabled in production)
# http://localhost:8000/api/v1/health

Running Without Docker (Local Dev)

python -m venv .venv
source .venv/bin/activate          # Windows: .venv\Scripts\activate
pip install -r requirements.txt

# Make sure PostgreSQL and Redis are running locally, then:
alembic upgrade head
uvicorn app.main:app --reload

Database Migrations

# Generate a new migration after changing a model
alembic revision --autogenerate -m "describe what changed"

# Apply all pending migrations
alembic upgrade head

# Roll back one migration
alembic downgrade -1

# Show migration history
alembic history --verbose

Rule: never call Base.metadata.create_all() outside of tests. All schema changes go through Alembic.


Adding a New Feature

The pattern is the same for every new domain entity:

  1. Modelapp/models/your_entity.py (inherit from Base)
  2. Register → add from app.models.your_entity import YourEntity to app/db/base.py
  3. Migrationalembic revision --autogenerate -m "add your_entity"
  4. Schemaapp/schemas/your_entity.py
  5. Repositoryapp/repositories/your_entity_repo.py (extend BaseRepository)
  6. Serviceapp/services/your_entity_service.py
  7. Routesapp/api/v1/routes/your_entity.py
  8. Register routes → add router.include_router(your_entity.router) in app/api/v1/router.py

Running Tests

# All tests
pytest

# With coverage report
pytest --cov=app --cov-report=term-missing

# Single file
pytest tests/api/v1/test_health.py -v

Tests use a rollback-per-test fixture so the DB is always clean. Set TEST_DATABASE_URL in your environment to point at a separate test database in CI.


Production Checklist

  • ENVIRONMENT=production
  • DEBUG=false
  • LOG_JSON=true
  • DATABASE_ECHO=false
  • ALLOWED_HOSTS set to your actual domain(s)
  • CORS_ORIGINS set to your actual frontend URL(s)
  • REDIS_PASSWORD set
  • .env is not committed to version control (check .gitignore)
  • docker-compose.prod.yml used (removes exposed DB/Redis ports, disables volume mounts)
  • Migrations applied: alembic upgrade head
  • Swagger UI disabled (automatic when ENVIRONMENT=production)
  • HTTPS terminated at reverse proxy (nginx/Caddy/ALB) — app itself does not handle TLS

Environment Variables Reference

See .env.example for the full list with descriptions. All variables are loaded via app/core/settings.py (Pydantic BaseSettings) — start there to understand defaults and validation rules.


License

MIT

About

Template to use in any scalable FastAPI project

Topics

Resources

License

Stars

Watchers

Forks

Contributors