API Design [PT-BR]
O Design de API é uma etapa crucial no desenvolvimento de sistemas, pois define como os componentes se comunicam entre si. Uma API bem projetada facilita a integração, a escalabilidade e a manutenção.
Neste contexto, exploraremos os princípios e as melhores práticas para criar APIs eficientes, intuitivas e robustas. Para elucidar o material os exemplos a seguir usam o contexto de uma Loja Virtual.
1 - Documentação
A documentação desempenha um papel crucial como um canal de comunicação entre os desenvolvedores que utilizam a API e os criadores da própria API. Ela oferece informações claras sobre como utilizar a API, quais endpoints estão disponíveis, quais parâmetros são aceitos e como interpretar as respostas. Uma documentação bem elaborada é essencial para evitar erros comuns e garantir que os desenvolvedores possam utilizar a API de forma eficaz.
Além disso, uma API bem documentada demonstra transparência e profissionalismo por parte dos criadores. Quando a API passa por evoluções (como a adição de novos recursos ou alterações), é fundamental atualizar a documentação para refletir essas mudanças. Uma boa prática é sempre disponibilizar a especificação OpenAPI (O famoso swagger 😅) da API, que oferece uma descrição detalhada dos endpoints, parâmetros e respostas esperadas. Isso facilita a vida dos desenvolvedores e contribui para um ambiente de desenvolvimento mais eficiente e confiável. Para mais informações acesse: https://swagger.io/specification/.
Curiosidade ⭐
Qual a diferença entre Swagger e OpenAPISpec?
OpenAPI = Especificação
Swagger = Ferramentas para implementar a especificação
2 - Suporte JSON
Utilize JSON como o formato padrão para transferência de dados. Isso garante compatibilidade com várias tecnologias e simplifica a manipulação dos dados tanto no lado do cliente quanto no servidor.
Em casos críticos de performance é aconselhável o uso de payloads baseados no protocolo gRPC que tráfega binário.
A recomendação é manter a nomenclatura dos atributos seguindo o padrão Camel Case.
Essa minha recomendação, pois nas empresas que trabalhei sempre seguiam esse padrão. Mas não tem uma regra explícita dizendo qual é melhor usar. O que existe é um senso baseado nas grandes empresas como Google, Facebook, Twitter (X) que o padrão snake-case é o mais recomendável.
No fim a regra é: abrace um padrão e siga ele em todas suas API. O que não pode é ficar mudando de padrão a cada API que vc constrói!
Exemplo:
{
"name": "Lucas",
"lastName": "Paixão",
"city": "Tabajara"
}
- Camel Case:
- Começa com a primeira letra minúscula e a primeira letra de cada nova palavra subsequente em maiúscula.
- Exemplo: coisasParaFazer, idadeDoAmigo, valorFinal.
- Pascal Case (também conhecido como “upper camel case” ou “capital case”):
- Todas as palavras começam com letra maiúscula.
- Exemplo: CoisasParaFazer, IdadeDoAmigo, ValorFinal.
- Snake Case (também conhecido como “underscore case”):
- Utiliza underline no lugar de espaço para separar as palavras.
- Exemplo: coisas_para_fazer, idade_do_amigo, valor_final.
3 - Use substantivos nos caminhos dos endpoints
Em vez de verbos, escolha nomes descritivos para os recursos. Em casos de múltiplos nomes, separe-os por “-“.
Ruim
GET /querycarts/123GET /getAllCustomersPOST /criateNewCustomerDELETE /deleteCustomerGET /userManagement/1Bom
GET /carts/123GET /customersPOST /customersDELETE /customers/1GET /user-management/14 - Use substantivos no plural
A questão de usar nomes no singular ou plural gera discussões frequentes. Em geral, é preferível nomes no plural, pois indicam um conjunto de características.
Ruim
GET /cart/123Bom
GET /carts/1235 - Idempotência
A idempotência refere-se à propriedade de uma operação que pode ser aplicada várias vezes, produzindo o mesmo resultado e sem alterar o estado do sistema além da primeira execução. Em termos de APIs REST, isso significa que uma chamada de API idempotente, independentemente do número de repetições, terá o mesmo efeito.
Em resumo, a idempotência é uma boa prática em serviços REST, garantindo que operações possam ser repetidas sem efeitos colaterais indesejados!
Verbos Idempotentes em APIs REST:
Os seguintes verbos HTTP são considerados idempotentes:
GET: Pode retornar a mesma resposta da primeira chamada N vezes.
PUT: Altera o estado de uma aplicação, mas sempre retorna a mesma resposta após a primeira chamada.
PATCH: Altera o estado de uma aplicação, mas sempre retorna a mesma resposta após a primeira chamada.
DELETE: Quando chamado, deleta um objeto e mantém o mesmo estado da aplicação para chamadas subsequentes.
HEAD, TRACE e OPTIONS também são idempotentes.
POST não é idempotente. Sua principal funcionalidade é criar um recurso, alterando o estado da aplicação a cada requisição.
Exemplo Prático:
Suponha um endpoint "/user" com o verbo POST que recebe o seguinte payload:
{
"name": "Lucas",
"lastName": "Paixão",
"city": "Tabajara"
}Cada chamada a esse endpoint criará um novo usuário no banco de dados, alterando o estado da aplicação.
Por outro lado, o GET, PATCH e o PUT são idempotentes, retornando a mesma resposta independentemente do número de chamadas.
6 - Utilizar corretamente os métodos HTTP
Separe seu API em recursos lógicos.
Manipule esses recursos usando os métodos HTTP apropriados:
GET: Recuperar informações de um recurso.
POST: Criar um novo recurso.
PUT: Atualizar um recurso existente.
PATCH: Atualizar parte de um recurso existente. Por exemplo, atualizar somente o nome do usuário.
DELETE: Excluir um recurso.
Por exemplo:
Solicitar /customers/563 com o método GET recupera um cliente específico.
Solicitar a mesma URL com o método DELETE exclui o cliente com código 563.
Ruim
GET /getAllCustomers GET /getCustomer/1POST /criateNewCustomerDELETE /deleteCustomerPUT /updateCustomerPATCH /patchCustomerBom
GET /customersGET /customers/1POST /customers {"name": "João Tabajara", "address": "Rua Tabajara"}DELETE /customers/1PUT /customers/1 {"name": "João Tabajara Queiroz", "address": "Rua Triste Feliz"}PATCH /customers/1 {"name": "João Tabajara Albuquerque"}7 - Utilizar subrecursos para relacionamentos
Quando há hierarquia de objetos e recursos, use subrecursos.
Ruim
GET /carts/1?item=1Bom
GET /carts/1/items/18 - Versionamento de API
O versionamento de APIs garante a estabilidade e a confiabilidade das suas interfaces. Quando uma API evolui, é importante comunicar de forma transparente as mudanças para os consumidores. O versionamento de APIs envolve gerenciar as alterações em uma API de maneira controlada e transparente. Ele garante que as versões antigas coexistam com as novas, sem quebrar aplicativos existentes. Afinal, você está entregando dados para o público, e eles precisam saber quando a maneira como esses dados são entregues muda.
Ruim
GET /carts/v1/123Bom
GET /v1/carts/123GET /carts/123?version=1GET /carts/123 {"Header": {"Accept-version": "v1"}}Existem várias razões para versionar sua API
Compatibilidade: À medida que a API evolui, você pode precisar fazer alterações incompatíveis com a versão anterior. O versionamento permite que as versões coexistam sem impactar aplicativos existentes.
Comunicação: O versionamento é uma forma de comunicação com os desenvolvedores. Ele informa quais mudanças ocorreram e como elas afetam a API.
Transparência: Versionar sua API demonstra transparência e responsabilidade. Os consumidores sabem que você está gerenciando as mudanças de forma controlada.
Aqui estão algumas estratégias comuns para versionar APIs
Prefixo de URL: Adicione um prefixo à URL da API para indicar a versão. Por exemplo:
Versão 1: /v1/products
Versão 2: /v2/products
Header de versão: Use um cabeçalho HTTP para especificar a versão desejada.
Parâmetro de versão
9 - Paginação
A paginação é uma técnica essencial quando se trata de projetar APIs que retornam grandes conjuntos de dados. Ela permite dividir os resultados em páginas menores, facilitando o manuseio e a exibição dos dados para os consumidores da API. Aqui estão algumas considerações importantes:
Ruim
GET /cartsBom
GET /carts?pageSize=XX&pageToken=xxMotivos de usar paginação
Quando uma API retorna muitos resultados, como uma lista de itens, é impraticável retornar todos os dados de uma só vez. A paginação permite que os resultados sejam divididos em partes gerenciáveis.
A paginação é especialmente útil para melhorar o desempenho e a eficiência da API, reduzindo a carga no servidor e a quantidade de dados transferidos pela rede.
Considerações adicionais
Certifique-se de que a documentação da sua API explique claramente como usar a paginação.
Lide com casos em que não há mais páginas (por exemplo, a última página) e forneça feedback adequado aos consumidores.
10 - Ordenação
Quando se lida com conjuntos de dados que precisam ser apresentados de maneira organizada é importante oferecer uma forma de ordená-los. Aqui estão algumas considerações relevantes:
Ao projetar uma API, é fundamental considerar como os recursos serão ordenados quando solicitados pelos clientes.
Por exemplo, se você está criando uma API para listar produtos, os clientes podem querer ordená-los por preço, nome ou data de lançamento.
Considere definir uma ordem padrão para os resultados quando nenhum critério de ordenação é especificado.
Certifique-se de que os nomes dos critérios sejam intuitivos e fáceis de entender.
Além do critério de ordenação, permita que os clientes especifiquem a direção da ordenação (ascendente ou descendente).
Ruim
GET /itemsBom
GET /items?sorteBy=time&order=ascGET /items?sorteBy=price&order=descGET /items?sorteBy=name&order=ascConsiderações adicionais
Lide com cenários em que os clientes fornecem critérios de ordenação inválidos.
Retorne mensagens de erro claras e instruções sobre como corrigir a solicitação.
11 - Filtros
Quando projetamos APIs, a capacidade de filtrar e buscar dados específicos é importante para atender às necessidades dos consumidores. Aqui estão algumas práticas importantes relacionadas aos filtros:
Os parâmetros de consulta são uma maneira comum de permitir que os clientes filtrem os resultados de uma API.
Por exemplo, ao buscar produtos, os clientes podem querer filtrar por categoria, preço, data de criação etc.
Documente quais campos podem ser usados como filtros na sua API.
Considere fornecer sugestões de valores para os campos de filtro, se aplicável.
Ofereça suporte a diferentes operadores de filtro, como igual, diferente, maior que, menor que etc.
Permita que os clientes combinem vários filtros para refinar ainda mais os resultados.
Valide os parâmetros de consulta para garantir que os valores sejam válidos e dentro dos limites aceitáveis.
Retorne mensagens de erro claras para solicitações inválidas.
Ruim
GET /productsBom
GET /products?filter=corlor:eq:redGET /products?filter=price:gt:50GET /products?filter=name:neq:Tabajara and price:gt:5012 - Limite de tráfego
O Rate Limiting é uma estratégia para restringir o tráfego de rede recebido por sua aplicação, limitando a quantidade de requisições realizadas em um certo período de tempo. Geralmente, isso é feito rastreando os endereços IP que realizam as requisições e definindo um limite para a quantidade de requisições permitidas dentro de um intervalo de tempo específico. Quando a quantidade de requisições excede o limite, novas requisições não são completadas por um período determinado. Essa abordagem é essencial para proteger contra atividades maliciosas, como ataques de bots, força bruta e DDoS.
Benefícios do Rate Limiting
Segurança: Protege sua aplicação contra sobrecarga de tráfego e ataques.
Estabilidade: Evita que recursos sejam consumidos excessivamente, mantendo a estabilidade do servidor.
Controle de Carga: Ajuda a manter um controle adequado da carga nos serviços web publicados.
13 - Circuit Breakers
APIs bem arquitetadas levam em consideração que o sistema final pode estar indisponível e criam mecanismos de proteção. Entre esses mecanismos, o circuit breaker desempenha um papel fundamental. Ele promove robustez, evita falhas em cascata e mantém a estabilidade do seu sistema
Resiliência e Estabilidade:
Quando uma API externa (ou qualquer serviço) está instável, o circuit breaker atua como um guardião.
Se a API começar a retornar erros repetidamente ou ficar indisponível, o circuit breaker intervém temporariamente, interrompendo as chamadas para essa API.
Isso evita sobrecarregar a API com chamadas desnecessárias e preserva a estabilidade do seu próprio serviço.
Prevenção de Overhead:
Sem um circuit breaker, sua aplicação continuaria tentando chamar a API problemática, resultando em overhead desnecessário.
O circuit breaker evita essas chamadas repetitivas, melhorando a eficiência e performance.
Transparência e Controle:
O circuit breaker permite que você defina regras para quando ele deve abrir ou fechar.
Ele registra os estados (aberto, fechado ou meio aberto) e permite ajustes com base nas condições da API.
Recuperação Automática:
Quando o circuit breaker está no estado meio aberto, ele faz chamadas de teste para verificar se a API está estável novamente.
Se a API se recuperar, o circuit breaker reabre a conexão automaticamente.
14 - Segurança
A segurança é um aspecto fundamental para proteger os dados, garantir a integridade das transações e evitar ameaças.
SSL/TLS (Transport Layer Security): Toda API deve usar TLS para criptografar as mensagens em trânsito. Isso protege as informações enviadas pela API e pelos usuários.
OAuth2: Para autenticação e autorização, o OAuth2 é amplamente utilizado. Ele oferece fluxos específicos para aplicativos da web, aplicativos móveis e outros dispositivos.
O OAuth2 com OpenID Connect é uma ótima opção para SSO, permitindo que os usuários façam login uma vez e acessem várias APIs.
API-KEY: As API keys são tokens de segurança que permitem que um usuário ou aplicativo acesse e utilize os recursos de uma API.
Elas são frequentemente incluídas nos cabeçalhos das solicitações para autenticar e autorizar o acesso.
As API keys não são consideradas totalmente seguras, pois geralmente são acessíveis aos clientes.
Se uma API key for roubada, ela pode ser usada indefinidamente, a menos que o proprietário do projeto a revogue ou a regenere.
Em resumo, o OAuth2 oferece maior segurança, flexibilidade e controle em comparação com as API keys. Desta forma, o uso do OAuth2 é recomendável sempre quando disponível.
15 - Controle de Acesso
Às vezes, é necessário garantir acessos específicos a recursos da sua API, e isso pode ser alcançado por meio do controle de acesso. Atualmente, existem dois tipos de controle de acesso comumente usados:
RBAC (Role-Based Access Control)
O RBAC concede ou nega acesso com base nos papéis do usuário dentro de uma organização.
Os papéis em RBAC geralmente se referem a grupos de pessoas que compartilham certas características, como:
Departamentos
Localizações
Níveis de senioridade
Funções de trabalho
Com um papel definido, você pode atribuir permissões, como:
Acesso (o que o usuário pode ver)
Operações (leitura, gravação, criação ou exclusão de arquivos)
Sessões (duração do login)
ABAC (Attribute-Based Access Control)
O ABAC responde à pergunta “O que essa pessoa pode fazer?” com base em:
Usuário: Características do usuário, como título do cargo, tarefas típicas ou nível de senioridade.
Atributos do recurso: Tipo de arquivo, criador do arquivo, sensibilidade do documento etc.
Ambiente: Local de acesso, horário do dia, data no calendário etc.
A escolha entre ABAC e RBAC dependerá das complexidades e requisitos específicos da sua API. Em alguns casos, combinar os dois métodos pode ser a melhor solução.
Em resumo:
RBAC controla acesso amplo em toda a organização com base em papéis.
ABAC adota uma abordagem mais detalhada, considerando atributos específicos do usuário, ambiente e recurso.
16 - HATEOAS
O HATEOAS (Hypermedia as the Engine of Application State) é um conceito que pode transformar a maneira como as APIs são consumidas e interagidas.
Auto-Descrição e Navegação:
O HATEOAS permite que uma API se torne autoexplicativa.
Os recursos da API incluem links de hipermídia nas respostas, permitindo que os clientes naveguem dinamicamente entre os recursos.
Isso melhora a descoberta e a usabilidade da API.
Exemplo Prático:
Imagine uma API de leitura de um cliente da loja virtual.
Sem HATEOAS: A resposta é um JSON com o ID e o Nome do cliente, somente.
{
"customerId": "10A",
"customerName": "Jane"
}Com HATEOAS: A resposta possui um atribruto _links que permitem que os clientes naveguem dinamicamente entre os recursos, descobrindo as ações disponíveis e melhorando a usabilidade da API. Inclusive levando em consideração controle de acesso!
{
"customerId": "10A",
"customerName": "Jane",
"_links": {
"self": {
"href": "/customers/10A"
},
"orders": {
"href": "/customers/10A/orders"
}
}
}Comparação com Hipertexto:
O HATEOAS segue o mesmo princípio do hipertexto na web.
Assim como os usuários da Internet navegam por links para encontrar informações, os clientes de APIs podem fazer o mesmo com recursos HATEOAS.
Em resumo, o HATEOAS torna as APIs mais flexíveis, autoexplicativas e dinâmicas. Ele reduz o acoplamento entre clientes e servidores, permitindo que os clientes descubram as ações disponíveis sem conhecimento prévio da estrutura da API.
A recomendação é avaliar se o uso de HATEOAS irá trazer benefícios reais aos usuários finais. Em APIs simples, por exemplo, o uso de HATEOAS pode trazer uma complexidade desnecessária para o backend sem ganhos reais para o usários finais.


Excelente o post. Começa bem simples mas vai fundo no detalhe. #respect
Conteúdo excelente!
Um bom guia pra ter sempre ao alcance durante o desenvolvimento.