💻 Exemplos: Capítulo 18: Documentação Automática OpenAPI/Swagger, Redoc e Healthchecks
Especialização em Backend com Python & FastAPI • Demonstrações Práticas Compiláveis
💻 Exemplo 1 — Metadados, Tags e responses= para Documentar Erros de Negócio
from fastapi import FastAPI, HTTPException, status
from pydantic import BaseModel, Field
tags_metadata = [
{"name": "Pedidos", "description": "Criação, consulta e cancelamento de pedidos."},
{"name": "Faturamento", "description": "Emissão de notas fiscais e cobrança."},
]
app = FastAPI(
title="Plataforma de Pedidos API",
description="API corporativa para gestão do ciclo de vida de pedidos.",
version="2.1.0",
openapi_tags=tags_metadata,
)
class ErroNegocio(BaseModel):
detail: str
class PedidoRequest(BaseModel):
item: str = Field(..., examples=["Mouse Ergonômico"])
quantidade: int = Field(..., ge=1, examples=[2])
valor_unitario: float = Field(..., gt=0, examples=[199.90])
class PedidoResponse(BaseModel):
id: int
item: str
total: float
_PEDIDOS = {1: {"item": "Mouse Ergonômico", "total": 399.80}}
@app.get(
"/pedidos/{pedido_id}",
response_model=PedidoResponse,
responses={404: {"model": ErroNegocio, "description": "Pedido não encontrado"}},
tags=["Pedidos"],
summary="Consultar pedido por ID",
)
async def obter_pedido(pedido_id: int) -> PedidoResponse:
pedido = _PEDIDOS.get(pedido_id)
if pedido is None:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Pedido não encontrado")
return PedidoResponse(id=pedido_id, **pedido)
@app.post("/pedidos", response_model=PedidoResponse, status_code=201, tags=["Pedidos"])
async def criar_pedido(payload: PedidoRequest) -> PedidoResponse:
novo_id = max(_PEDIDOS) + 1
total = payload.quantidade * payload.valor_unitario
_PEDIDOS[novo_id] = {"item": payload.item, "total": total}
return PedidoResponse(id=novo_id, item=payload.item, total=total)
💻 Exemplo 2 — Healthcheck Raso (liveness) vs. Profundo (readiness)
import asyncio
from fastapi import APIRouter, FastAPI, status
from fastapi.responses import JSONResponse
app = FastAPI()
router = APIRouter(tags=["Infra"])
async def checar_banco() -> bool:
await asyncio.sleep(0.01) # simula round-trip de um SELECT 1
return True
async def checar_redis() -> bool:
await asyncio.sleep(0.01) # simula round-trip de um PING
return True
@router.get("/health")
async def health_raso():
# Liveness: só confirma que o processo está de pé, sem tocar dependências externas.
return {"status": "ALIVE"}
@router.get("/health/deep")
async def health_profundo():
# Readiness: verifica dependências reais antes de aceitar tráfego.
db_ok, redis_ok = await asyncio.gather(checar_banco(), checar_redis())
saudavel = db_ok and redis_ok
corpo = {
"status": "HEALTHY" if saudavel else "UNHEALTHY",
"checks": {"database": db_ok, "redis": redis_ok},
}
codigo = status.HTTP_200_OK if saudavel else status.HTTP_503_SERVICE_UNAVAILABLE
return JSONResponse(content=corpo, status_code=codigo)
app.include_router(router)
💻 Exemplo 3 — Desativar Documentação em Produção e Exportar openapi.json para CI/CD
import json
import os
from fastapi import FastAPI
ambiente = os.getenv("ENV", "development")
app = FastAPI(
title="Plataforma de Pedidos API",
version="2.1.0",
docs_url=None if ambiente == "production" else "/docs",
redoc_url=None if ambiente == "production" else "/redoc",
openapi_url=None if ambiente == "production" else "/openapi.json",
)
@app.get("/status")
async def status_api():
return {"ambiente": ambiente}
def exportar_schema_para_arquivo(caminho: str = "openapi.json") -> int:
"""Gera o schema OpenAPI estático para consumo por geradores de SDK (CI/CD)."""
schema = app.openapi()
conteudo = json.dumps(schema, indent=2, ensure_ascii=False)
with open(caminho, "w", encoding="utf-8") as arquivo:
arquivo.write(conteudo)
return len(schema.get("paths", {}))
if __name__ == "__main__":
total_rotas = exportar_schema_para_arquivo()
print(f"Schema OpenAPI exportado com sucesso ({total_rotas} rotas mapeadas).")