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érioRESTGraphQLgRPC
Formato de dadosJSON (texto)JSON (texto)Protobuf (binário)
TransporteHTTP/1.1 ou HTTP/2HTTP/1.1 ou HTTP/2HTTP/2
TipagemExterna (OpenAPI)Nativa (schema)Nativa (protobuf)
Caso de uso idealAPIs públicas, CRUDFrontends com dados complexosComunicação entre serviços
DesempenhoBomVariávelExcelente
CacheNativo HTTPRequer solução customNão padronizado
Curva de aprendizadoBaixaMédiaMé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

TecnologiaFerramenta de contrato
RESTOpenAPI (Swagger)
GraphQLSDL (Schema Definition Language)
gRPCProtocol Buffers (.proto)
EventosAsyncAPI

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.