Skip to content

GrandGaleTechnologies/behemoth-fastapi

Repository files navigation

πŸš€ Behemoth FastAPI

A powerful, scalable template to kickstart your backend projects. Includes FastAPI with Docker integration, JWT authentication, optional Logfire instrumentation, and PEP-582-based dependency management via Astral's uv.

Inspired by Radoslav Georgiev's Django Structure for Scale lecture and my own experience, this template offers a structured approach to building scalable web applications.

πŸ“‘ Table of Contents

✨ Features

  • FastAPI template with JWT authentication and Alembic migrations.
  • Docker & docker-compose configs for zero-friction container development.
  • Astral uv for dependency installation and script execution (no manual venv activation).
  • Logfire auto-instrumentation: set LOGFIRE_TOKEN in your environment and the app will automatically send logs to Pydantic Logfire.
  • Modular structure inspired by scaling best practices.
  • Optional uvloop integration for improved asyncio performance (Linux/macOS only).

πŸ“ Project Structure

.vscode/
alembic/
app/
  author/
    routes/
      __init__.py
      base.py
    schemas/
      __init__.py
      base.py
      create.py
      edit.py
      response.py
    annotations.py
    apis.py
    crud.py
    exceptions.py
    formatters.py
    models.py
    selectors.py
    services.py
  common/
    annotations.py
    auth.py
    cache.py
    crud.py
    dependencies.py
    exceptions.py
    paginators.py
    schemas.py
    security.py
    types.py
    utils.py
  core/
    database.py
    handlers.py
    settings.py
    tags.py
  external/
    main.py
tests/
.env_sample
.flake8
.gitignore
.pylintrc
.python-version
alembic.ini
auto-module.py
docker-compose.yml
Dockerfile
LICENSE
pyproject.toml
pytest.ini
railway.toml
README.md
requirements.txt
start.sh
uv.lock

πŸ’‘ Getting Started

Prerequisites

  • Docker & Docker Compose (optional)
  • uv installed globally

1. Clone the repository

git clone https://github.com/GrandGaleTechnologies/behemoth-fastapi
cd behemoth-fastapi

2. Install dependencies

Using uv (recommended)

# Optional: add uvloop (doesnt work well on windows)
uv add uvloop
uv venv

3. Environment variables

Create a .env file and add environment variables (use Environment Variables as a guide).

4. Initialize the database

# With uv
uv run alembic upgrade head

5. Start the application

Development mode

uv run fastapi dev

Production mode

uv run fastapi run

πŸ› οΈ Using auto-module.py

This script automates creation of new FastAPI modules with a consistent folder layout:

app/
└── ModuleName/
    β”œβ”€β”€ routes/__init__.py
    β”œβ”€β”€ routes/base.py
    β”œβ”€β”€ schemas/base.py
    β”œβ”€β”€ schemas/create.py
    β”œβ”€β”€ schemas/edit.py
    β”œβ”€β”€ schemas/response.py
    β”œβ”€β”€ apis.py
    β”œβ”€β”€ models.py
    β”œβ”€β”€ services.py
    β”œβ”€β”€ selectors.py
    β”œβ”€β”€ exceptions.py
    └── formatters.py

To create a new module:

uv run auto-module.py

Follow the prompts to specify the module name.

πŸ”§ Environment Variables

See configuration and instructions in docs/ENV.md. Use .env_sample as your template.

πŸ” JWT Auth & Security

JWT Authentication

  • Implemented in common/auth.py and common/security.py
  • Leverages FastAPI's Depends and reusable get_current_user() function
  • Tokens include expiration and are signed using a secret key in .env

Secure Endpoints

To protect a route:

from common.dependencies import get_current_user

@app.get("/secure-data")
def secure_data(user: User = Depends(get_current_user)):
    return {"message": f"Hello, {user.username}!"}

Auth Flow Overview

  1. User logs in via /login endpoint β†’ receives JWT access token
  2. Frontend stores token (e.g., in localStorage or Authorization header)
  3. Token is sent with each protected request
  4. Backend validates token and grants access

Rate Limiting

This project uses Redis-based rate limiting through fastapi-limiter. By default, it allows 3 requests per second per endpoint.

Redis Setup with Docker (Single Container)

You can run Redis directly as a standalone container:

docker run -d \
  --name behemoth-redis \
  -p 6379:6379 \
  -v redis_data:/data \
  redis:latest

This will:

  • Run Redis in detached mode (-d)
  • Name the container behemoth-redis
  • Expose Redis on port 6379
  • Persist data in a Docker-managed volume (redis_data)

Verify Redis is Running

docker exec -it behemoth-redis redis-cli ping

You should see PONG as the response.


Environment Configuration

Update your .env file with the Redis container URL (since it’s exposed on localhost):

REDIS_BROKER_URL=redis://localhost:6379/0

Testing Rate Limits

  1. Swagger UI

    • Navigate to http://localhost:8000
    • Make multiple rapid requests to any endpoint
    • After 3 requests within 1 second, you’ll receive a 429 Too Many Requests
  2. Curl

for i in {1..4}; do curl http://localhost:8000/health; done

Monitoring Redis

docker exec -it behemoth-redis redis-cli monitor

Troubleshooting

If Redis connection fails:

  1. Check container status:
docker ps -f name=behemoth-redis
  1. View logs:
docker logs behemoth-redis
  1. Restart Redis:
docker restart behemoth-redis

Environment Configuration

Update your .env file with the Redis container URL:

REDIS_BROKER_URL=redis://redis:6379/0

Rate Limiting Configuration

Rate limiting is configured in app/main.py:

REQ_RATE = 3        # Number of requests allowed
REQ_RATE_TIME = 1   # Time window in seconds

This means each endpoint allows 3 requests per second. After exceeding this limit, requests will receive a 429 (Too Many Requests) response.

Testing Rate Limits

  1. Swagger UI:

    • Navigate to http://localhost:8000
    • Make multiple rapid requests to any endpoint
    • After 3 requests within 1 second, you'll receive a 429 response
  2. Using Docker CLI:

# Make multiple requests quickly
for ($i = 1; $i -le 4; $i++) {
    docker-compose exec api curl http://localhost:8000/health
}

Monitoring Rate Limits

Monitor Redis rate limiting in real-time:

docker-compose exec redis redis-cli monitor

Caching

```python
from app.common.cache import CacheManager
from app.sample_module.schemas import SampleModel # Example Pydantic model

# Initialize for a specific model with a TTL of 300 seconds
sample_cache_manager = CacheManager(ttl=300, model_class=SampleModel)

# Or initialize without a model_class if you just need to store/retrieve raw data
generic_cache_manager = CacheManager(ttl=60)
```
  1. Set Data in Cache:

    # For sample_cache_manager (with SampleModel)
    await sample_cache_manager.set(
      data={"id": 1}, 
      value=SampleModel(id=1, name="Test"), 
      cache_prefix="sample:"
    )
    
    # For generic_cache_manager (raw dict)
    await generic_cache_manager.set(
      data={"key": "my_data"}, 
      value={"message": "Hello, cached world!"}, 
      cache_prefix="generic:"
    )
  2. Get Data from Cache:

    # For sample_cache_manager (will return SampleModel instance or None)
    cached_sample = await sample_cache_manager.get(data={"id": 1}, cache_prefix="sample:")
    if cached_sample:
        print(f"Cached Sample: {cached_sample.name}")
    
    # For generic_cache_manager (will return dict or None)
    cached_generic = await generic_cache_manager.get(
      data={"key": "my_data"}, 
      cache_prefix="generic:"
    )
    if cached_generic:
        print(f"Cached Generic: {cached_generic['message']}")
    

Troubleshooting

If Redis connection fails:

  1. Check Redis container status:
docker-compose ps redis
  1. View Redis logs:
docker-compose logs redis
  1. Verify Redis network connectivity:
docker-compose exec api ping redis
  1. Check Redis container health:
docker inspect -f '{{.State.Health.Status}}' behemoth-fastapi-redis-1
  1. Restart Redis

Pre-commit Setup

This project uses pre-commit hooks to ensure code quality. The hooks include Ruff for linting and formatting, and Flake8 for additional code style checks.

Installation

  1. Install pre-commit:
uv add --dev pre-commit
  1. Install the pre-commit hooks:
pre-commit install

Usage

  • The hooks will run automatically on git commit
  • To manually run the hooks on all files:
pre-commit run --all-files
  • To run specific hooks:
pre-commit run ruff --all-files
pre-commit run flake8 --all-files

Configuration

Pre-commit configuration is stored in .pre-commit-config.yaml and includes:

  • Ruff for linting and formatting
  • Flake8 for additional code style checks

πŸŽ— License

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

πŸš€ Deploy

Deploy this template on Railway:

Deploy on Railway

🀝 Contribute to the Project

Contributions are welcome! Fork the repo, create a branch, and submit a PR. Engage in discussions for ideas and improvements.

πŸ“¬ Contact

Releases

No releases published

Packages

No packages published

Contributors 4

  •  
  •  
  •  
  •