APIs são o tecido conectivo da internet moderna. Cada vez que você abre um aplicativo no celular, faz uma compra online ou verifica uma notificação, dezenas de chamadas de API estão acontecendo nos bastidores. O design de uma API impacta diretamente a produtividade dos desenvolvedores que a consomem, a performance da aplicação e a fácilidade de evolução do sistema ao longo do tempo.
Neste artigo, vamos explorar as melhores práticas para design de APIs tanto no paradigma REST quanto no GraphQL. Não se trata de escolher um vencedor entre os dois, mas de entender quando cada abordagem brilha e como implementa-la da melhor forma possível.
Fundamentos de um bom design de API
Independente de você escolher REST ou GraphQL, alguns princípios fundamentais se aplicam a qualquer API bem projetada. Esses princípios guiam decisões de design e ajudam a criar interfaces que são intuitivas, previssiveis e fáceis de manter.
Consistência
Uma API consistente é previsível. Se um endpoint usa camelCase para nomes de campos, todos os endpoints devem seguir a mesma convenção. Se datas são formatadas em ISO 8601 em uma resposta, devem ser assim em todas as respostas. A consistência reduz a carga cognitiva dos consumidores e diminui erros de integração.
Simplicidade
Uma API deve ser fácil de entender e usar corretamente. Nomes de recursos e operações devem ser intuitivos. A estrutura de dados deve refletir o domínio do negócio, não a estrutura interna do banco de dados. Quando em duvida, prefira simplicidade a flexibilidade.
Evolucionabilidade
APIs precisam evoluir sem quebrar os consumidores existentes. Isso requer planejamento cuidadoso de versionamento, deprecação progressiva e comúnicação clara de mudanças. Uma API que não pode evoluir e uma API que eventualmente sera substituida.
"Uma boa API não e aquela que faz tudo; e aquela que torna as coisas comuns simples e as coisas difíceis possíveis." - Adaptado de Alan Kay
REST: princípios e boas práticas
REST (Representational State Transfer) continua sendo o paradigma mais popular para APIs web. Sua simplicidade conceitual e o uso de padrões HTTP bem estabelecidos o tornam uma escolha sólida para a maioria dos casos de uso.
Design de URLs
URLs devem representar recursos (substantivos), não ações (verbos). Os métodos HTTP já expressam a ação desejada. Compare:
- Errado:
POST /criarUsuário,GET /buscarProjetos - Correto:
POST /usuários,GET /projetos
Use plurais para coleções de recursos e IDs para recursos individuais: /projetos lista todos os projetos, /projetos/123 retorna um projeto específico. Para sub-recursos, use aninhamento: /projetos/123/tarefas lista as tarefas do projeto 123.
Métodos HTTP corretos
Cada método HTTP tem uma semantica específica que deve ser respeitada:
- GET: Recuperar recursos. Deve ser seguro (sem efeitos colaterais) e idempotente
- POST: Criar novos recursos. Não idempotente
- PUT: Substituir completamente um recurso existente. Idempotente
- PATCH: Atualizar parcialmente um recurso. Pode ou não ser idempotente
- DELETE: Remover um recurso. Idempotente
Códigos de status HTTP
Use códigos de status semanticamente corretos. Não retorne 200 OK com um corpo contendo {"error": "Not found"}. Os códigos mais importantes:
- 200 OK: Requisição bem sucedida
- 201 Created: Recurso criado com sucesso (resposta a POST)
- 204 No Content: Sucessó sem corpo de resposta (comum em DELETE)
- 400 Bad Request: Erro de validação ou requisição malformada
- 401 Unauthorized: Autenticação necessária ou inválida
- 403 Forbidden: Autenticado, mas sem permissão
- 404 Not Found: Recurso não encontrado
- 409 Conflict: Conflito com estado atual do recurso
- 422 Unprocessable Entity: Validação de negócio falhou
- 429 Too Many Requests: Rate limit excedido
- 500 Internal Server Error: Erro inesperado no servidor
Paginação
Nunca retorne coleções inteiras sem paginação. As três abordagens mais comuns são:
- Offset-based:
?page=2&limit=20- Simples, mas problematica para grandes volumes de dados - Cursor-based:
?cursor=abc123&limit=20- Mais eficiente e consistente, ideal para feeds e listas em tempo real - Keyset-based: Similar ao cursor, mas usando valores de colunas reais para paginação
Inclua metadados de paginação na resposta: total de registros, página atual, próxima página e links de navegação seguindo o padrão HATEOAS quando possível.
Versionamento
Existem três estratégias principais de versionamento:
- URL path:
/v1/projetos- A mais explícita é fácil de entender - Header:
Accept: application/vnd.API+json;version=1- Mais RESTful, mas menos visível - Query parameter:
/projetos?version=1- Fácil de usar, mas poluiu a URL
A recomendação pragmatica e usar versionamento na URL path. E o mais simples de implementar, testar e documentar.
GraphQL: quando e como usar
GraphQL, desenvolvido pelo Facebook e lancado como open source em 2015, oferece uma abordagem fundamentalmente diferente para APIs. Em vez de múltiplos endpoints, GraphQL expoe um único endpoint onde o cliente define exatamente quais dados precisa.
Vantagens do GraphQL
- Sem over-fetching: O cliente recebe exatamente os campos solicitados, nada mais
- Sem under-fetching: Uma única query pode buscar dados de múltiplos recursos relacionados
- Tipagem forte: O schema define tipos e validação é automática
- Evolução sem versionamento: Novos campos podem ser adicionados sem afetar queries existentes
- Introspeccao: O schema e auto-documentado e pode ser explorado interativamente
Quando GraphQL brilha
GraphQL e particularmente valiosó em cenários onde:
- Múltiplos clientes (web, mobile, TV) consomem a mesma API com necessidades de dados diferentes
- A interface e altamente interativa e requer dados de múltiplas fontes em uma única tela
- A equipe de frontend quer autonomia para iterar na interface sem depender de mudanças no backend
- O grafo de dados é complexo com muitos relacionamentos entre entidades
Desafios do GraphQL
GraphQL não e uma bala de prata. Alguns desafios importantes:
- Complexidade de queries: Queries profundamente aninhadas podem causar problemas de performance. Implemente limites de profundidade e complexidade
- Caching: O caching HTTP padrão não funciona bem com GraphQL. Ferramentas como Apollo Client e Relay implementam cache no cliente
- Upload de arquivos: Não ha suporte nativo. Multipart requests ou endpoints REST dedicados são necessários
- Rate limiting: Mais complexo que em REST, pois uma única query pode ter custo computacional variável
Tratamento de erros
O tratamento de erros e frequentemente negligenciado no design de APIs, mas e crucial para a experiência do desenvolvedor. Respostas de erro devem ser consistentes, informativas e acionáveis.
Estrutura de erro para REST
Uma boa resposta de erro inclui: código de erro único, mensagem legivel para humanos, detalhes técnicos para debugging e, idealmente, um link para documentação relevante. Padronize a estrutura e use-a consistentemente em todos os endpoints.
Erros em GraphQL
GraphQL tem sua própria específicação para erros que inclui mensagem, localização no query e path do campo que causou o erro. Além disso, e comum usar a extensão extensions para incluir códigos de erro customizados e metadados adicionais.
Autenticação e autorização
A escolha do mecanismo de autenticação impacta significativamente a segurança e a usabilidade da API. As abordagens mais comuns são:
- JWT (JSON Web Tokens): Tokens auto-contidos que não requerem consulta ao banco para validação. Ideais para arquiteturas distribuídas
- OAuth 2.0: Padrão para autorização delegada. Essencial quando terceiros precisam acessar dados em nome do usuário
- API Keys: Simples e adequadas para integração servidor-a-servidor. Não use para autenticação de usuários finais
Ferramentas como o GalagoWork utilizam JWT combinado com refresh tokens para manter sessões seguras e proporcionar uma experiência fluida para o usuário, sem comprometer a segurança da API.
Documentação
Uma API sem documentação e uma API inútilizavel. A documentação deve ser precisa, atualizada e incluir exemplos práticos. Ferramentas que geram documentação a partir do código ajudam a manter a sincronia.
- OpenAPI/Swagger: O padrão para documentação de APIs REST. Permite gerar documentação interativa, SDKs e testes
- GraphQL Playground/GraphiQL: IDEs no navegador que permitem explorar o schema, testar queries e visualizar documentação gerada automaticamente
- Postman Collections: Coleções de requisições que servem como documentação executável e podem ser compartilhadas entre a equipe
Performance e otimização
APIs lentas prejudicam a experiência do usuário e aumentam custos de infraestrutura. Algumas estratégias essenciais de otimização:
- Compressao: Use gzip ou brotli para reduzir o tamanho das respostas
- Cache: Implemente cache em múltiplas camadas (CDN, reverse proxy, aplicação, banco de dados)
- Campos esparsos: Em REST, permita que o cliente selecione quais campos deseja:
?fields=id,nome,email - DataLoader: Em GraphQL, use DataLoader para evitar o problema N+1 de queries ao banco
- Connection pooling: Reutilize conexões com o banco de dados em vez de criar novas a cada requisição
Conclusão: REST ou GraphQL?
A resposta mais honesta e: depende. REST e excelente para APIs públicas, integração entre sistemas e cenários onde o caching HTTP padrão é importante. GraphQL brilha em aplicações com interfaces ricas, múltiplos clientes e grafos de dados complexos. Muitas organizações usam ambos: REST para APIs públicas e GraphQL para comúnicação interna entre frontend e backend.
O mais importante não e a tecnologia escolhida, mas a disciplina no design. Uma API REST bem projetada e infinitamente melhor que uma API GraphQL mal implementada, e vice-versa. Invista tempo no design, documente rigorosamente e trate sua API como um produto que tem desenvolvedores como usuários.