Skip to content

gsnoopy/fastapi-template

Repository files navigation

FastAPI Template

Template de API REST com autenticação JWT, rate limiting, observabilidade e arquitetura modular. Pronto para produção.


Stack

Tecnologia Versão Por quê
Python 3.14 uuid.uuid7 nativo (UUIDs ordenados sem lib externa)
FastAPI ≥0.115 Performance, tipagem nativa, OpenAPI automático
SQLAlchemy ≥2.0 ORM maduro com AsyncSession — queries não bloqueiam o event loop
asyncpg ≥0.30 Driver PostgreSQL nativo async (mais rápido que psycopg2)
Alembic ≥1.13 Migrações de schema versionadas
PostgreSQL Banco principal; Neon como provider cloud
Redis ≥5.0 Refresh tokens opacos + contadores de rate limit
bcrypt ≥4.0 Hash de senha com salt automático
PyJWT ≥2.8 Geração e validação de JWT
slowapi ≥0.1.9 Rate limiting por IP via decorador
Sentry SDK ≥2.0 Captura de erros em produção com tracing
prometheus-fastapi-instrumentator ≥7.0 Métricas HTTP expostas em /metrics
python-json-logger ≥3.0 Logs estruturados em JSON
pydantic-settings ≥2.0 Configuração via variáveis de ambiente tipada
Docker + Compose Ambiente reproduzível com Redis embutido

Arquitetura

app/
├── core/
│   ├── config.py          # Settings via .env (pydantic-settings)
│   ├── database.py        # Engine PostgreSQL + get_db dependency
│   ├── exceptions.py      # Exceções de domínio + handlers HTTP
│   ├── middleware.py      # Correlation ID, security headers, X-Response-Time
│   ├── logging_config.py  # JSON logging com request_id injetado
│   └── context.py         # ContextVar para request_id entre camadas
├── models/
│   └── user.py            # Modelo SQLAlchemy (UUIDv7, soft delete)
├── modules/
│   ├── auth/              # Login, refresh, logout
│   │   ├── router.py
│   │   ├── service.py     # Lógica de autenticação + account lockout
│   │   └── schema.py
│   ├── user/              # CRUD de usuários
│   │   ├── router.py
│   │   ├── service.py
│   │   ├── repository.py
│   │   └── schema.py
│   └── health/            # Health check
└── security/
    ├── jwt.py             # create_access_token / decode_token
    ├── refresh.py         # Refresh tokens opacos no Redis
    ├── dependencies.py    # get_current_user / require_roles
    └── limiter.py         # Instância global do slowapi Limiter

Padrão por módulo: router → service → repository

  • Router: recebe requisição, valida input, delega ao service
  • Service: lógica de negócio, levanta exceções de domínio
  • Repository: acesso ao banco, sem lógica de negócio

Autenticação

Dois tokens complementares:

Access Token (JWT, 15 min)

  • Stateless — verificado localmente sem tocar o banco ou Redis
  • Contém apenas o user_id no campo sub
  • Curto para minimizar janela de abuso caso seja vazado

Refresh Token (opaco, 30 dias)

  • UUID4 armazenado no Redis com TTL
  • Rotacionado a cada uso (o token anterior é invalidado)
  • Permite logout real e revogação imediata

Account Lockout

  • Após LOGIN_MAX_ATTEMPTS falhas consecutivas, a conta é bloqueada por LOGIN_LOCKOUT_SECONDS
  • Implementado com INCR + EXPIRE no Redis — sem tabela extra no banco

Segurança

Recurso Implementação
Senha hasheada bcrypt com salt automático
RBAC require_roles(UserRole.ADMIN) como Depends
Soft delete is_active=False em vez de DELETE — preserva integridade referencial
Security headers X-Content-Type-Options, X-Frame-Options, X-XSS-Protection, Referrer-Policy, Permissions-Policy
Rate limiting slowapi — 5/min no login, 200/min global
CORS Configurável via CORS_ORIGINS no .env
Docs desabilitadas /docs, /redoc e /openapi.json só existem com DEBUG=true

IDs — UUIDv7

Os IDs usam uuid.uuid7 (Python 3.14 stdlib).

UUIDv4 é aleatório — inserções em B-tree causam fragmentação e page splits. UUIDv7 é time-ordered (primeiros 48 bits = timestamp ms) — inserções são sequenciais, índice cresce como um BIGSERIAL mas com unicidade global.


Observabilidade

Logs (JSON estruturado)

{"timestamp": "...", "level": "INFO", "logger": "app.core.middleware",
 "message": "POST /v1/auth/login | status=200 duration_ms=42.1",
 "request_id": "3e4a1b2c-..."}

Cada requisição gera um X-Request-ID (ou usa o header recebido) propagado via ContextVar para todos os logs da requisição.

Métricas (Prometheus + Grafana Cloud)

Endpoint /metrics exposto automaticamente com contadores, histogramas de latência e status codes por rota.

Para visualizar no Grafana Cloud (free tier):

  1. Crie uma conta em grafana.com
  2. Vá em Connections → Add new connection → Prometheus
  3. Aponte o scrape URL para https://sua-api.com/metrics
  4. Importe o dashboard FastAPI Observability (ID [16110](https://grafana.com/grafana/dashboards/16110))

Em dev local, o endpoint fica em http://localhost:8000/metrics.

Sentry Erros não tratados são capturados com stack trace e enviados ao Sentry. Configurado com traces_sample_rate=0.2 (20% das transações para performance tracing).


Variáveis de Ambiente

Copie .env.example para .env e preencha:

APP_NAME=Minha API
DEBUG=false

DB_HOST=seu-host.neon.tech
DB_PORT=5432
DB_USER=usuario
DB_PASSWORD=senha
DB_NAME=nome_do_banco
DB_SSL=true

SECRET_KEY=troque-em-producao
ACCESS_TOKEN_EXPIRE_MINUTES=15
REFRESH_TOKEN_EXPIRE_DAYS=30

CORS_ORIGINS=["https://meufront.com"]

REDIS_URL=redis://localhost:6379

SENTRY_DSN=https://...@sentry.io/...

LOGIN_MAX_ATTEMPTS=5
LOGIN_LOCKOUT_SECONDS=300

Rodar com Docker

docker compose up --build

O Redis sobe junto. O container da API aguarda o Redis estar saudável antes de iniciar (healthcheck + condition: service_healthy).


Desenvolvimento local

pip install -r requirements-dev.txt
uvicorn main:app --reload

Endpoints

Método Rota Auth Descrição
GET / Versão da API
GET /v1/health Health check
POST /v1/auth/login Login (rate limit: 5/min)
POST /v1/auth/refresh Rotaciona refresh token
POST /v1/auth/logout Revoga refresh token
GET /v1/users/me Bearer Usuário autenticado
GET /v1/users Admin Lista usuários (paginado)
GET /v1/users/{id} Admin Busca usuário por ID
POST /v1/users Admin Cria usuário
PATCH /v1/users/{id} Admin Atualiza usuário
DELETE /v1/users/{id} Admin Desativa usuário (soft delete)
GET /metrics Métricas Prometheus

Adicionar um novo módulo

  1. Crie app/modules/meumodulo/ com router.py, service.py, repository.py, schema.py
  2. Adicione o modelo em app/models/
  3. Registre o router em main.py:
    app.include_router(meumodulo_router, prefix=API_PREFIX)
  4. Gere a migration:
    alembic revision --autogenerate -m "add meumodulo"
    alembic upgrade head

Todos os métodos de repositório e service devem ser async def. Use await ao chamar operações de banco (await self.db.execute(...), await self.db.commit()).

About

Async FastAPI template

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors