Princípios de design de API: RESTful, GraphQL e além
1. Fundamentos do design de APIs modernas
O design de APIs é a arte de criar interfaces de comunicação entre sistemas que sejam previsíveis, consistentes e evolutivas. Aplicar princípios sólidos desde o início determina a longevidade de uma API, reduzindo custos de manutenção e retrabalho. Três pilares sustentam esse design: contratos formais, maturidade arquitetural e documentação viva.
Contratos formais como OpenAPI Specification (para REST) e Schema Definition Language (para GraphQL) permitem gerar documentação automática, clientes SDK e testes de conformidade. O modelo de maturidade de Richardson classifica APIs REST em níveis: Nível 0 (túnel HTTP), Nível 1 (recursos), Nível 2 (verbos HTTP) e Nível 3 (HATEOAS). GraphQL emerge como uma evolução que combina consultas flexíveis com tipagem forte.
# Exemplo de contrato OpenAPI (REST)
openapi: 3.0.0
info:
title: API de Usuários
version: 1.0.0
paths:
/users/{id}:
get:
parameters:
- name: id
in: path
required: true
schema:
type: integer
responses:
'200':
description: Usuário encontrado
content:
application/json:
schema:
$ref: '#/components/schemas/User'
components:
schemas:
User:
type: object
properties:
id:
type: integer
name:
type: string
email:
type: string
2. RESTful: arquitetura baseada em recursos e verbos HTTP
REST (Representational State Transfer) organiza APIs em torno de recursos identificados por URIs. Cada recurso suporta operações padronizadas via verbos HTTP: GET (leitura), POST (criação), PUT (atualização completa), PATCH (atualização parcial) e DELETE (remoção).
HATEOAS (Hypermedia as the Engine of Application State) eleva o design ao Nível 3 de Richardson, onde as respostas incluem links para navegação entre recursos. Isso torna a API auto-descritiva e reduz o acoplamento cliente-servidor.
# Exemplo de resposta REST com HATEOAS
GET /users/42
{
"id": 42,
"name": "Maria Silva",
"email": "maria@exemplo.com",
"_links": {
"self": { "href": "/users/42" },
"orders": { "href": "/users/42/orders" },
"update": { "href": "/users/42", "method": "PUT" }
}
}
O tratamento de erros deve usar códigos HTTP semânticos (400 para requisição inválida, 404 para recurso não encontrado, 500 para erro interno) e incluir mensagens padronizadas no corpo da resposta. O versionamento pode ser feito via URI (/v1/users), cabeçalho (Accept: application/vnd.api.v1+json) ou parâmetro de consulta.
3. GraphQL: consultas flexíveis e tipagem forte
GraphQL permite que o cliente especifique exatamente quais dados deseja, eliminando over-fetching e under-fetching. O schema define tipos, queries (consultas), mutations (alterações) e subscriptions (eventos em tempo real).
# Exemplo de schema GraphQL
type User {
id: ID!
name: String!
email: String!
orders: [Order!]!
}
type Order {
id: ID!
total: Float!
items: [OrderItem!]!
}
type Query {
user(id: ID!): User
users(page: Int, limit: Int): [User!]!
}
type Mutation {
createUser(name: String!, email: String!): User!
}
type Subscription {
userCreated: User!
}
O problema N+1 ocorre quando uma query aninhada gera múltiplas consultas ao banco de dados. DataLoader resolve isso com batching e caching em nível de requisição. A paginação em GraphQL usa o padrão "cursor-based" com argumentos first, after, last e before.
# Consulta GraphQL com paginação
{
users(first: 10, after: "cursor_abc") {
edges {
node {
id
name
email
}
cursor
}
pageInfo {
hasNextPage
endCursor
}
}
}
Autenticação e autorização são implementadas via middleware que injeta o contexto da requisição, e diretivas como @auth ou @hasRole podem ser definidas no schema para controle granular.
4. Além do binômio REST vs GraphQL: abordagens híbridas e emergentes
gRPC utiliza Protocol Buffers para serialização binária eficiente e suporta streaming bidirecional. É ideal para comunicação entre microsserviços que exigem baixa latência.
# Definição de serviço gRPC
service UserService {
rpc GetUser (GetUserRequest) returns (User);
rpc ListUsers (ListUsersRequest) returns (stream User);
rpc CreateUsers (stream CreateUserRequest) returns (UserSummary);
}
message GetUserRequest {
int32 id = 1;
}
message User {
int32 id = 1;
string name = 2;
string email = 3;
}
Falcor e tRPC oferecem APIs com tipagem segura onde o cliente chama funções como se fossem locais, especialmente em ecossistemas TypeScript. APIs baseadas em eventos (WebSocket, SSE) complementam REST/GraphQL para notificações em tempo real.
5. Padrões de design transversais e governança
Versionamento semântico (MAJOR.MINOR.PATCH) guia a evolução da API. Deprecação usa cabeçalhos Sunset e Deprecation, além de documentar alternativas. Segurança exige OAuth 2.0 com fluxos apropriados (Authorization Code para apps web, Client Credentials para serviços), OpenID Connect para autenticação, rate limiting (por IP, token ou rota) e validação rigorosa de entrada.
Observabilidade inclui logging estruturado (JSON), métricas de latência (p50, p95, p99) e tracing distribuído com OpenTelemetry para rastrear requisições entre microsserviços.
6. Casos de uso e critérios de escolha entre os paradigmas
- Aplicações CRUD tradicionais: REST é o padrão consolidado, com amplo suporte em ferramentas e frameworks.
- Interfaces dinâmicas e mobile: GraphQL reduz over-fetching e under-fetching, permitindo que cada tela busque exatamente os dados necessários.
- Microsserviços internos: gRPC oferece performance superior com serialização binária e streaming eficiente.
- APIs públicas: REST com OpenAPI facilita adoção por terceiros e geração de documentação interativa.
- Sistemas reativos: Eventos via WebSocket ou SSE combinados com REST/GraphQL para cenários híbridos.
7. Tendências futuras e evolução contínua
APIs auto-descritivas com JSON Schema e AsyncAPI permitem contratos vivos que evoluem junto com o sistema. A integração com IA e LLMs transforma APIs em superfícies para agentes autônomos, que consomem endpoints programaticamente. Padrões de resiliência como Circuit Breaker, Retry com backoff exponencial e Bulkhead garantem estabilidade em cenários de falha.
O futuro aponta para APIs que combinam múltiplos paradigmas: REST para operações CRUD, GraphQL para consultas complexas, gRPC para alta performance e eventos para comunicação assíncrona, tudo governado por contratos formais e observabilidade.
Referências
- OpenAPI Specification — Documentação oficial do padrão OpenAPI para descrição de APIs RESTful.
- GraphQL Specification — Especificação oficial da linguagem de consulta GraphQL, incluindo schema, queries e mutations.
- gRPC Documentation — Guia oficial do gRPC, com exemplos de Protocol Buffers e streaming bidirecional.
- Richardson Maturity Model — Artigo de Martin Fowler sobre os níveis de maturidade de APIs REST.
- DataLoader Library — Repositório oficial do DataLoader para batching e caching em GraphQL, resolvendo o problema N+1.
- OAuth 2.0 Authorization Framework — Documentação e especificações do protocolo OAuth 2.0 para autorização em APIs.
- OpenTelemetry Documentation — Guia oficial para implementação de tracing distribuído e observabilidade em APIs.