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).")