API Versioning - C-Suite
Visão Geral
Este documento descreve o padrão de versionamento de APIs para o ecossistema C-Suite. O objetivo é facilitar a evolução das APIs sem quebrar clientes existentes.
Módulo de Versionamento
Localização
O módulo principal está em: common_api_versioning.py (raiz do projeto)
Uso Básico
from fastapi import FastAPI
from common_api_versioning import create_versioned_router
app = FastAPI()
# Criar router versionado
v1_router = create_versioned_router("v1", tags=["API v1"])
# Registrar endpoints no router versionado
@v1_router.post("/decide")
async def decide_v1(...):
...
# Incluir router no app
app.include_router(v1_router)
Estratégia de Versionamento
URL Path Versioning (Recomendado)
Versão no path da URL: /v1/decide, /v2/decide
Vantagens:
- Simples e explícito
- Fácil de entender
- Suportado por todos os clientes HTTP
Exemplo:
v1_router = create_versioned_router("v1")
@v1_router.post("/decide")
async def decide_v1(...):
...
Header Versioning (Alternativo)
Versão no header HTTP: X-API-Version: v1
Uso:
from common_api_versioning import get_api_version_from_request
@app.post("/decide")
async def decide(request: Request, ...):
version = get_api_version_from_request(request)
if version == "v1":
return await decide_v1_logic(...)
elif version == "v2":
return await decide_v2_logic(...)
Padrão de Versionamento
Estrutura de Versões
- v1: Versão inicial/atual (padrão)
- v2: Próxima versão (quando necessário)
- v3+: Versões futuras
Regras
- Versão atual é sempre a mais recente
- Versões antigas são mantidas por período de depreciação
- Breaking changes requerem nova versão
- Non-breaking changes podem ser feitas na versão atual
Quando Criar Nova Versão
Criar nova versão quando:
- Remover campos de resposta
- Mudar tipos de dados
- Mudar comportamento significativo
- Remover endpoints
Não criar nova versão quando:
- Adicionar novos campos opcionais
- Adicionar novos endpoints
- Melhorar performance (sem mudar contrato)
- Corrigir bugs
Exemplos de Implementação
Exemplo 1: Router Versionado Simples
from fastapi import FastAPI
from common_api_versioning import create_versioned_router
app = FastAPI()
# Router v1
v1_router = create_versioned_router("v1", tags=["API v1"])
@v1_router.post("/decide")
async def decide_v1(req: DecideRequest):
# Lógica v1
return {"version": "v1", "result": ...}
app.include_router(v1_router)
Exemplo 2: Múltiplas Versões
from fastapi import FastAPI
from common_api_versioning import create_versioned_routers
app = FastAPI()
# Criar routers para v1 e v2
routers = create_versioned_routers(["v1", "v2"])
# Endpoint v1
@routers["v1"].post("/decide")
async def decide_v1(req: DecideRequest):
return {"version": "v1", "result": ...}
# Endpoint v2 (com melhorias)
@routers["v2"].post("/decide")
async def decide_v2(req: DecideRequestV2):
return {"version": "v2", "result": ..., "metadata": ...}
# Incluir ambos
app.include_router(routers["v1"])
app.include_router(routers["v2"])
Exemplo 3: Endpoints Não Versionados (Legacy)
Para manter compatibilidade, endpoints sem versão podem redirecionar para v1:
from fastapi import FastAPI
from common_api_versioning import create_versioned_router
app = FastAPI()
# Router v1
v1_router = create_versioned_router("v1")
@v1_router.post("/decide")
async def decide_v1(req: DecideRequest):
return {"version": "v1", "result": ...}
# Endpoint legacy (sem versão) - redireciona para v1
@app.post("/decide")
async def decide_legacy(req: DecideRequest):
"""Endpoint legacy - use /v1/decide"""
return await decide_v1(req)
app.include_router(v1_router)
Exemplo 4: Versionamento com Validação
from fastapi import FastAPI, HTTPException
from common_api_versioning import (
create_versioned_router,
validate_api_version,
get_api_version_from_request
)
app = FastAPI()
@app.post("/decide")
async def decide(request: Request, req: DecideRequest):
version = get_api_version_from_request(request)
if not validate_api_version(version):
raise HTTPException(
status_code=400,
detail=f"Versão {version} não suportada. Versões disponíveis: v1"
)
if version == "v1":
return await decide_v1_logic(req)
else:
return await decide_v1_logic(req) # Fallback para v1
Migração de Código Existente
Antes (sem versionamento)
@app.post("/decide")
async def decide(req: DecideRequest):
return {"result": ...}
Depois (com versionamento)
from common_api_versioning import create_versioned_router
v1_router = create_versioned_router("v1")
@v1_router.post("/decide")
async def decide_v1(req: DecideRequest):
return {"version": "v1", "result": ...}
# Manter endpoint legacy por compatibilidade
@app.post("/decide")
async def decide_legacy(req: DecideRequest):
return await decide_v1(req)
app.include_router(v1_router)
Documentação OpenAPI
Versões na Documentação Swagger
Com routers versionados, o Swagger/OpenAPI automaticamente organiza endpoints por versão:
/docs
├── API v1
│ ├── POST /v1/decide
│ └── POST /v1/decide/auto
└── API v2 (quando criado)
├── POST /v2/decide
└── POST /v2/decide/auto
Depreciação de Versões
Processo de Depreciação
- Anunciar depreciação: Adicionar header
Deprecation: truenas respostas - Período de aviso: Manter versão por 6-12 meses
- Remover versão: Após período de aviso
Exemplo de Depreciação
from fastapi import Response
@v1_router.post("/decide")
async def decide_v1(req: DecideRequest, response: Response):
# Adicionar header de depreciação
response.headers["Deprecation"] = "true"
response.headers["Sunset"] = "2026-12-01" # Data de remoção
return {"version": "v1", "result": ...}
Versionamento por App
4c Decision API
# Versão atual: v1
v1_router = create_versioned_router("v1", tags=["Decision API v1"])
@v1_router.post("/decide")
async def decide_v1(...):
...
@v1_router.post("/decide/auto")
async def decide_auto_v1(...):
...
csuite-executive
# Versão atual: v1
v1_router = create_versioned_router("v1", tags=["C-Suite API v1"])
@v1_router.get("/policies/{org_id}")
async def list_policies_v1(...):
...
@v1_router.post("/policies/{org_id}")
async def create_policy_v1(...):
...
4c-suite
# Versão atual: v1
v1_router = create_versioned_router("v1", tags=["4C-Suite API v1"])
@v1_router.get("/organismo/{org_id}/status")
async def organismo_status_v1(...):
...
Boas Práticas
- Sempre versionar novas APIs desde o início
- Manter versões antigas durante período de transição
- Documentar breaking changes claramente
- Usar versionamento consistente em todos os apps
- Incluir versão na resposta (opcional, mas útil)
- Testar todas as versões suportadas
Próximos Passos
- ✅ Módulo de versionamento criado
- ⏳ Migrar endpoints existentes para usar versionamento
- ⏳ Adicionar versionamento em todos os apps
- ⏳ Documentar breaking changes entre versões
- ⏳ Criar processo de depreciação