Documentação e OpenAPI
O contrato da API como arquivo versionado, de onde saem documentação, clientes e testes.
As decisões sobre o contrato da API são quatro: de onde o arquivo nasce, como garantir que ele diz a verdade, como evitar mudanças que quebram quem integra e o que publicar a partir dele.
De onde o arquivo nasce
| Gerado pelo código | Escrito antes do código | Gerado de esquemas compartilhados | |
|---|---|---|---|
| Fonte da verdade | Rotas e tipos | O arquivo OpenAPI | Esquemas (Zod, Pydantic, structs) usados para validar e documentar |
| Esforço inicial | Mínimo | Escrever e revisar o arquivo | Médio |
| Revisão do contrato | Depois do código pronto | Antes do código, por quem vai integrar | Junto com o código |
| Risco típico | Documentar o que não é aplicado | O código se afasta do arquivo | Recursos do framework fora do esquema |
| Bom para | APIs internas, um time só | APIs públicas e parceiros, vários times | A maioria dos projetos novos |
A terceira coluna é a que mais reduz divergências: o mesmo esquema que valida a entrada gera a documentação. No teste do nível Conceito, Fastify, FastAPI e huma funcionam assim, e as divergências encontradas neles foram de detalhe. No ASP.NET Core 9, as anotações iam para a documentação, mas não validavam nada, e a especificação prometia regras que não existiam.
Escrever o arquivo antes compensa quando o contrato precisa ser discutido com quem vai usar a API antes de existir código: parceiros, outro time, o aplicativo de celular. Os dois lados combinam o arquivo, o front-end começa com um servidor de mentira, e o back-end implementa o combinado.
Como garantir que o arquivo diz a verdade
| Técnica | O que confere | Quando roda |
|---|---|---|
| Verificador de estilo (Redocly CLI, Spectral) | A forma do arquivo: campos obrigatórios da especificação, nomes, convenções do time | A cada mudança no arquivo |
| Teste de contrato (Schemathesis) | Se a API responde o que o arquivo promete, com pedidos gerados a partir dele | Na integração contínua, contra a API em execução |
| Validação em tempo de execução | Recusa pedidos, e opcionalmente respostas, que não batem com o arquivo | Em cada pedido |
| Tipos gerados no cliente | Se o código do cliente usa os campos e tipos certos | Na compilação do cliente |
Nenhuma técnica sozinha basta. O verificador de estilo não sabe o que a API faz. O teste de contrato não sabe se o arquivo está bem escrito. As duas juntas, na integração contínua, pegam a maior parte dos problemas do nível Conceito.
O teste de contrato tem um efeito colateral útil: ele confere os dois lados. No teste, o FastAPI recusou um total "0.00" que o contrato permitia, e a correção era no contrato, que tinha esquecido a regra.
Regras do time no verificador
Os verificadores aceitam regras próprias. As que mais valem a pena, porque pegam os erros do nível Conceito:
| Regra | Por quê |
|---|---|
Todo esquema de objeto declara required | Clientes gerados sem campos obrigatórios tratam tudo como opcional |
Valores monetários não usam number | number vira ponto flutuante na maioria das linguagens |
| Toda operação documenta pelo menos um 4xx | Erros fazem parte do contrato |
Respostas de erro usam application/problem+json | Um formato de erro único para todas as rotas |
Campos em snake_case (ou camelCase), sem misturar | Consistência para quem integra |
Como evitar mudanças que quebram
| Mudança | Compatível? |
|---|---|
| Nova rota, novo campo opcional na entrada, novo campo na resposta | Sim |
| Novo campo obrigatório na entrada | Não |
| Remover ou renomear um campo da resposta | Não |
| Mudar o tipo de um campo | Não |
Novo valor num enum da resposta | Não, para clientes que tratam todos os valores |
| Restringir a validação (máximo menor, padrão mais rígido) | Não |
Com o arquivo versionado no repositório, uma etapa da integração contínua compara o contrato da mudança com o da versão publicada e falha quando há uma mudança incompatível. No teste, o oasdiff classificou corretamente as quatro mudanças do nível Conceito. Quando a mudança é necessária, ela passa a ser uma decisão explícita: uma nova versão da API, como descreve o tópico de versionamento.
Para os enum que vão crescer, avise desde o começo. O oasdiff sugere a extensão x-extensible-enum, e a documentação deve dizer que valores novos podem aparecer e que o cliente deve tratar valores desconhecidos.
O que publicar a partir do arquivo
| Saída | Para quem | Cuidados |
|---|---|---|
| Documentação navegável (Swagger UI, Redoc, Scalar) | Quem integra | Exemplos reais em cada operação; descrição dos erros |
| Tipos e clientes gerados | Quem consome, inclusive o próprio front-end | Gerar na integração contínua, não à mão; fixar a versão do gerador |
| Servidor de mentira (Prism) | Front-end e parceiros, antes da API existir | Usar os exemplos do arquivo |
| Coleção de pedidos (Postman, Bruno, Insomnia) | Testes manuais | Importar do arquivo, em vez de manter à parte |
Gerar só os tipos costuma valer mais que gerar o cliente inteiro. Com o openapi-typescript, o compilador recusou no teste um total numérico e um campo com o nome errado, sem nenhum código gerado em execução. Clientes completos, do openapi-generator, ajudam em linguagens sem uma boa biblioteca HTTP, mas produzem muito código para revisar e atualizar.
Resumo da decisão
- Esquemas compartilhados para validar e documentar com a mesma fonte, ou o arquivo antes do código quando o contrato precisa ser combinado com outros times.
- Verificador de estilo com as regras do time, a cada mudança no arquivo.
- Teste de contrato na integração contínua, contra a API em execução.
- Comparação de versões do contrato, falhando em mudanças incompatíveis.
- Tipos gerados para os clientes, inclusive o próprio front-end.
- Erros documentados, com um formato único.
O próximo nível mostra as cinco APIs de exemplo, o contrato escrito à mão e os comandos de verificação, teste de contrato e comparação de versões.
Prompts para aprofundar
Ao abrir no ChatGPT ou no Claude, o prompt é enviado ao serviço escolhido pela URL e também fica copiado na sua área de transferência.
Escolher como manter o contrato
Quero definir como o contrato OpenAPI da minha API vai ser produzido e mantido. Pergunte qual linguagem e framework eu uso, quem consome a API (front-end próprio, outros times, parceiros, público) e se já existe algum arquivo OpenAPI. Recomende para o meu caso, explicando o porquê: 1. Gerar pelo código, escrever antes ou usar esquemas compartilhados. 2. Como garantir que o arquivo e o código concordam. 3. Quais verificações rodar na integração contínua. 4. O que publicar a partir do arquivo: documentação, tipos, servidor de mentira.
Regras de estilo para o contrato
Quero criar regras de estilo para os contratos OpenAPI do meu time, para rodar num verificador como o Redocly CLI ou o Spectral. Pergunte quais convenções o time já segue (nomes de campos, formato de erros, paginação, datas, dinheiro). Depois proponha um conjunto de regras, explicando o problema que cada uma evita, e escreva a configuração para o verificador que eu escolher. Inclua pelo menos: required em todos os objetos, dinheiro fora de number, erros documentados em todas as operações e um formato único de erro.
Gerar clientes a partir do contrato
Quero gerar clientes ou tipos a partir do contrato OpenAPI da minha API. Pergunte em quais linguagens estão os consumidores e como eles chamam a API hoje. Compare para o meu caso: 1. Gerar só os tipos ou gerar o cliente completo. 2. As ferramentas de cada linguagem. 3. Onde gerar: na integração contínua, num pacote publicado, ou no próprio projeto consumidor. 4. Como lidar com valores novos em enum e campos novos sem quebrar os clientes.