Api Versioning

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

Regras

  1. Versão atual é sempre a mais recente
  2. Versões antigas são mantidas por período de depreciação
  3. Breaking changes requerem nova versão
  4. 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

  1. Anunciar depreciação: Adicionar header Deprecation: true nas respostas
  2. Período de aviso: Manter versão por 6-12 meses
  3. 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

  1. Sempre versionar novas APIs desde o início
  2. Manter versões antigas durante período de transição
  3. Documentar breaking changes claramente
  4. Usar versionamento consistente em todos os apps
  5. Incluir versão na resposta (opcional, mas útil)
  6. Testar todas as versões suportadas

Próximos Passos

  1. ✅ Módulo de versionamento criado
  2. ⏳ Migrar endpoints existentes para usar versionamento
  3. ⏳ Adicionar versionamento em todos os apps
  4. ⏳ Documentar breaking changes entre versões
  5. ⏳ Criar processo de depreciação

🔊 Text-to-Speech

1.0x
1.0
Pronto para reproduzir