APIs e contratos
Design de APIs, comparação entre REST, GraphQL e gRPC, versionamento de APIs e a abordagem contract-first.
Comunicação entre componentes
Em uma arquitetura moderna, os componentes não precisam apenas existir; precisam se comunicar de forma clara, estável e compreensível. É aí que entram as APIs e os contratos.
As decisões sobre APIs afetam diretamente a manutenibilidade, a escalabilidade e a experiência de desenvolvimento de todo o sistema.
O que é uma API?
Uma API é uma interface que permite que um sistema ou componente exponha capacidades a outros.
Em termos simples, uma API define:
- O que você pode pedir
- Como você deve pedir
- Qual resposta você pode esperar
- Quais erros podem ocorrer
Em qualquer sistema distribuído, as APIs são o mecanismo de comunicação entre componentes. Uma API bem projetada atua como um contrato: define o que um consumidor pode pedir, o que vai receber e sob quais condições.
REST (Representational State Transfer)
REST é o estilo arquitetural mais utilizado para APIs web. Baseia-se em recursos identificados por URLs e operações padrão HTTP.
Princípios fundamentais
- Recursos: Tudo é um recurso identificado por uma URI (
/usuarios/123). - Verbos HTTP: GET (ler), POST (criar), PUT (substituir), PATCH (atualizar parcialmente), DELETE (remover).
- Sem estado: Cada requisição contém toda a informação necessária; o servidor não guarda estado de sessão.
- Representações: Um recurso pode ter múltiplas representações (JSON, XML).
Exemplo de API REST
GET /api/pedidos → Lista de pedidos
GET /api/pedidos/42 → Detalhe do pedido 42
POST /api/pedidos → Criar novo pedido
PATCH /api/pedidos/42 → Atualizar pedido 42
DELETE /api/pedidos/42 → Remover pedido 42
Quando é útil
- APIs simples ou médias
- Integração entre serviços
- Interfaces públicas ou privadas bem definidas
- Quando você precisa de cache HTTP nativo
Vantagens do REST
- Amplamente adotado e compreendido pela indústria.
- Cacheável de forma nativa com headers HTTP.
- Ferramentas maduras para documentação (OpenAPI/Swagger).
- Funciona bem com a infraestrutura web existente (proxies, CDNs, load balancers).
Desvantagens do REST
- Over-fetching: O endpoint retorna mais dados do que o cliente precisa.
- Under-fetching: O cliente precisa fazer múltiplas requisições para obter dados relacionados.
- Sem tipagem nativa; depende de documentação externa para definir a estrutura.
GraphQL
GraphQL é uma linguagem de consulta para APIs que permite ao cliente especificar exatamente quais dados precisa.
Como funciona
# O cliente pede exatamente o que precisa
query {
pedido(id: 42) {
id
fecha
total
cliente {
nombre
email
}
items {
producto
cantidad
}
}
}
Vantagens do GraphQL
- Sem over-fetching nem under-fetching: O cliente define o formato da resposta.
- Um único endpoint: Todas as consultas vão para
/graphql. - Tipagem forte: O schema define tipos, campos e relações de forma explícita.
- Introspecção: O cliente pode descobrir o schema disponível em tempo de execução.
- Muito adequado em BFFs para agregação de dados.
Custos do GraphQL
- Complexidade no servidor: Resolver queries aninhadas pode gerar problemas de desempenho (N+1 queries).
- Cache difícil: Por não usar URLs únicas por recurso, o cache HTTP padrão não se aplica diretamente.
- Curva de aprendizado: Exige entender um novo paradigma tanto no cliente quanto no servidor.
- Segurança: Queries complexas podem ser usadas para ataques de negação de serviço se não forem limitadas.
- Controle mais delicado de performance e observabilidade um pouco diferente do REST.
gRPC
gRPC é um framework de comunicação de alto desempenho que usa Protocol Buffers (protobuf) para serialização e HTTP/2 como transporte.
Definição do serviço
syntax = "proto3";
service ServicioPedidos {
rpc ObtenerPedido (PedidoRequest) returns (PedidoResponse);
rpc CrearPedido (CrearPedidoRequest) returns (PedidoResponse);
rpc ListarPedidos (ListarRequest) returns (stream PedidoResponse);
}
message PedidoRequest {
int32 id = 1;
}
message PedidoResponse {
int32 id = 1;
string fecha = 2;
double total = 3;
}
Vantagens do gRPC
- Alto desempenho: A serialização binária (protobuf) é mais rápida e compacta que JSON.
- Streaming bidirecional: Suporta comunicação em tempo real com HTTP/2.
- Geração de código: A partir do arquivo
.proto, são gerados clientes e servidores em múltiplas linguagens. - Contratos estritos: O schema protobuf é o contrato; não há ambiguidade.
Desvantagens do gRPC
- Não funciona em navegadores diretamente (precisa de gRPC-Web como proxy).
- Depuração difícil: As mensagens binárias não são legíveis sem ferramentas especiais.
- Menor adoção: Menos ferramentas e documentação em comparação com REST.
- Complexidade de setup: Exige compilador de protobuf e configuração adicional.
Comparação direta
| Critério | REST | GraphQL | gRPC |
|---|---|---|---|
| Formato de dados | JSON (texto) | JSON (texto) | Protobuf (binário) |
| Transporte | HTTP/1.1 ou HTTP/2 | HTTP/1.1 ou HTTP/2 | HTTP/2 |
| Tipagem | Externa (OpenAPI) | Nativa (schema) | Nativa (protobuf) |
| Caso de uso ideal | APIs públicas, CRUD | Frontends com dados complexos | Comunicação entre serviços |
| Desempenho | Bom | Variável | Excelente |
| Cache | Nativo HTTP | Requer solução custom | Não padronizado |
| Curva de aprendizado | Baixa | Média | Média-Alta |
O que é um contrato
Um contrato é o acordo entre produtor e consumidor. Pode definir:
- Estrutura de request e response
- Campos obrigatórios e opcionais
- Tipos de dados
- Erros possíveis
- Headers necessários
- Regras de versionamento
Um contrato reduz a ambiguidade e ajuda a evoluir sistemas sem quebrar consumidores.
Contratos em eventos
Não são apenas as APIs que têm contratos. Os eventos também precisam de esquemas claros.
Por exemplo, se um serviço publica OrderPlaced, outros serviços precisam saber:
- Quais campos ele contém
- O que eles significam
- Quais são obrigatórios
- Como o schema evolui
Ferramentas de contrato por tecnologia
| Tecnologia | Ferramenta de contrato |
|---|---|
| REST | OpenAPI (Swagger) |
| GraphQL | SDL (Schema Definition Language) |
| gRPC | Protocol Buffers (.proto) |
| Eventos | AsyncAPI |
Design Contract-First
A abordagem contract-first (contrato primeiro) propõe definir a API antes de implementá-la. Em vez de escrever código e gerar documentação depois, você define o contrato e então implementa contra ele.
Fluxo contract-first
1. Definir o contrato (OpenAPI, protobuf, GraphQL schema)
↓
2. Revisar e validar com consumidores
↓
3. Gerar código base (servidor e cliente)
↓
4. Implementar a lógica de negócio
↓
5. Validar que a implementação cumpre o contrato
Vantagens do contract-first
- Desenvolvimento paralelo: Frontend e backend podem trabalhar simultaneamente usando o contrato como referência.
- Documentação sempre atualizada: O contrato é a documentação.
- Validação automática: Ferramentas podem verificar se a implementação cumpre o contrato.
- Melhor comunicação: O contrato é um artefato compartilhado entre equipes.
Evolução e versionamento
Os contratos mudam com o tempo. Por isso é importante pensar em:
- Compatibilidade retroativa: As mudanças não devem quebrar os consumidores existentes.
- Depreciação gradual: Marque endpoints ou campos como deprecated e dê tempo para migrar.
- Mudanças breaking: Identifique quando uma mudança é incompatível e exige uma nova versão.
- Ownership do contrato: Defina quem é responsável por manter e evoluir cada contrato.
Estratégias comuns de versionamento
Versionamento por URL
GET /api/v1/pedidos
GET /api/v2/pedidos
É a estratégia mais simples e explícita. O consumidor sabe exatamente qual versão está usando.
Versionamento por header
GET /api/pedidos
Accept: application/vnd.miapi.v2+json
Mantém as URLs limpas, mas é menos visível e mais difícil de testar no navegador.
Versionamento por query parameter
GET /api/pedidos?version=2
Simples, mas pode interferir no cache e não é considerado uma boa prática por muitas equipes.
Boas práticas de versionamento
- Evite mudanças que quebram: Adicione campos novos em vez de modificar os existentes.
- Deprecie antes de remover: Marque endpoints como deprecated e dê tempo aos consumidores para migrar.
- Documente as mudanças: Mantenha um changelog claro entre versões.
- Limite as versões ativas: Não mantenha mais de 2-3 versões simultâneas.
Por que os contratos importam
Sem contratos claros:
- As equipes interpretam coisas diferentes
- Surgem integrações frágeis
- As mudanças quebram consumidores
- Cresce o acoplamento acidental
Com contratos bem definidos:
- Há menos ambiguidade
- A evolução se torna mais segura
- As equipes podem se coordenar melhor
Princípios de bom design de APIs
Independentemente da tecnologia escolhida, estas práticas se aplicam a qualquer API:
- Consistência: Use convenções uniformes em nomes, formatos e códigos de erro.
- Idempotência: As operações GET, PUT e DELETE devem produzir o mesmo resultado se executadas múltiplas vezes.
- Tratamento de erros claro: Retorne códigos de status apropriados e mensagens de erro úteis.
- Paginação: Nunca retorne coleções completas sem limite; implemente paginação desde o início.
- Segurança: Autenticação (quem você é) e autorização (o que você pode fazer) em cada endpoint.
Qual escolher?
- REST se você precisa de uma API pública, simples e amplamente compatível.
- GraphQL se o seu frontend precisa de flexibilidade para consultar dados complexos e relacionados.
- gRPC se você precisa de comunicação de alto desempenho entre serviços internos.
Em muitos sistemas reais, as três tecnologias coexistem: REST para APIs públicas, GraphQL para o BFF que alimenta o frontend, e gRPC para comunicação interna entre microsserviços.
Resumo
As APIs permitem a comunicação entre componentes. Os contratos tornam essa comunicação explícita, manutenível e evolutiva. Em sistemas distribuídos, pensar bem os contratos é tão importante quanto escrever o código que os implementa.