O que um Júnior precisa saber de APIs
Métodos HTTP, path params x query params x body, status codes, idempotência, rate limit e documentação. Os conceitos de API que todo júnior devia dominar e que muita gente pula.
Tem uma porção de conceitos sobre criação de API que deveriam ser básicos para qualquer júnior, mas que na prática nem todo mundo domina. Vale até para quem já é júnior avançado ou pleno. Vou passar pelos principais aqui.
REST domina o mercado
Hoje em dia existem vários tipos de API (REST, GraphQL, SOAP, entre outros), mas na prática a esmagadora maioria do que você vai encontrar no mercado é REST. GraphQL aparece numa fatia bem menor, e o resto se divide entre os demais formatos. Por isso o foco aqui é REST, com algum comentário pontual sobre GraphQL no fim.
Numa API REST, toda a dinâmica gira em torno de dois momentos: a requisição, que é o que você manda, e a resposta, que é o que a API devolve.
A requisição: URL, método e o que cada um significa
Uma requisição começa com um endereço, algo como site.com/api/users. Junto desse endereço vai um método HTTP, e é o método que diz o que você quer fazer com aquele recurso.
- GET: buscar informação.
GET /api/userspega a lista de usuários (ou um usuário específico, dependendo de como a URL é montada). - POST: adicionar um recurso novo. Criar um usuário, registrar um pagamento, cadastrar um produto.
- PUT: alterar um recurso por completo. Se o produto tem título, descrição, preço e quantidade, um PUT espera que você mande os quatro campos, porque ele substitui o recurso inteiro.
- PATCH: alterar um recurso parcialmente. Quero mudar só o preço, uso PATCH e mando só o preço.
- DELETE: remover um recurso.
Na teoria a diferença entre PUT e PATCH é clara. Na prática, boa parte das APIs usa só PUT (ou só PATCH) para qualquer tipo de alteração, completa ou parcial, porque seguir a convenção à risca em todo lugar às vezes complica mais do que ajuda na manutenção. É bom conhecer a diferença, mas não estranhe quando vir um projeto que ignora essa separação.
DELETE: exclusão de verdade x soft delete
Quando uma API recebe um DELETE, ela pode fazer duas coisas por baixo dos panos:
- Hard delete: apaga o registro do banco de fato. Não existe mais.
- Soft delete: não apaga nada, só marca o registro como deletado, geralmente com uma coluna booleana (
deletado: true) ou um campo de status.
Um soft delete faz o registro sumir das consultas normais (um GET na lista de usuários não vai trazer os deletados), mas o dado continua existindo no banco. Sistemas que precisam manter histórico por questão de auditoria, ou que lidam com pedidos e pagamentos, costumam usar soft delete: o cliente cancelou uma compra, mas a loja não quer perder o registro daquilo para sempre.
Headers: informação que não é o dado principal da requisição
O header carrega informação adicional que não faz parte do "conteúdo" da requisição. O caso mais comum é autenticação: para criar, alterar ou deletar algo, normalmente é preciso estar logado, e isso é resolvido mandando um header Authorization com um token de acesso.
Uma requisição de API costuma ser stateless, ou seja, ela não depende de sessão nem de cookie guardado em algum lugar. Tudo que ela precisa para funcionar vai dentro dela mesma, incluindo essa informação de autenticação.
Onde os dados vão: path, query ou body
Isso aqui é um dos pontos que mais confunde quem está começando. Existem três lugares onde dá para mandar dado numa requisição, e cada um tem seu propósito.
Path parameters: parâmetros dentro da própria URL, usados para identificar um recurso específico. Em GET /api/users/123, o 123 é o ID do usuário que eu quero.
Query parameters: usados para filtro, ordenação, paginação, qualquer coisa que module um conjunto de resultados sem identificar um item específico. Em GET /api/users?ordenado=data, estou pedindo a lista de usuários, só que ordenada de um jeito específico.
Body: usado para mandar uma quantidade maior de dado, ou dado que não faz sentido (ou não cabe) numa URL: um JSON com as informações de um produto, um arquivo de upload, o conteúdo de um post de blog com 10 mil palavras. GET praticamente nunca leva body, já que GET serve para buscar informação, não para enviar. Body aparece sobretudo em POST e PATCH/PUT. DELETE, por sua vez, normalmente identifica o recurso pelo path, não pelo body.
A resposta: formato e o padrão de erro/resultado
A resposta de uma API REST hoje em dia é quase sempre JSON (XML ainda aparece, mas é raridade). Um formato comum, embora não seja um padrão oficial, é retornar algo como:
{
"error": null,
"data": { }
}O campo de erro vem preenchido quando algo deu errado, e o de dado vem preenchido quando deu certo. Cada API monta isso do jeito que faz sentido para ela, mas a ideia de separar "deu erro" de "resultado" se repete bastante.
Status code: como saber se deu certo sem ler o corpo da resposta
Toda resposta de uma API vem acompanhada de um status code, um número que indica o que aconteceu. Nenhum desses status é automático: é o programador que decide, no código, quando retornar cada um deles.
| Código | Nome | Quando usar |
|---|---|---|
| 200 | OK | Deu tudo certo, aqui está o resultado. |
| 201 | Created | Um recurso novo foi criado com sucesso (resposta típica de um POST). |
| 400 | Bad Request | A requisição veio errada ou incompleta, faltou um campo obrigatório. |
| 401 | Unauthorized | Faltou autenticação, o usuário precisa estar logado. |
| 403 | Forbidden | O usuário está autenticado, mas não tem permissão para aquilo. |
| 404 | Not Found | O endereço acessado não existe. |
| 405 | Method Not Allowed | O método usado não é permitido naquele endpoint (mandou GET onde só existe POST, por exemplo). |
| 500 | Internal Server Error | O problema foi do servidor, não de quem fez a requisição. |
Existem status na faixa 300 também (redirecionamento de URL), mas eles quase não aparecem em API, então não entram nessa lista.
Documentação: o cliente da sua API precisa saber o que ela faz
Se você cria uma API e não documenta nada, ninguém de fora sabe o que ela é capaz de fazer, mesmo que existam recursos ótimos ali dentro. As duas ferramentas mais usadas hoje para gerar documentação automaticamente são o OpenAPI (não confundir com OpenAI) e o Swagger. Você integra uma delas no seu projeto, faz uma configuração mínima, e ela lê o código da API para gerar um documento (às vezes até uma página inteira) descrevendo cada endpoint, método, parâmetro esperado e resposta possível.
Rate limit: controlando quantas vezes algo pode ser feito
Rate limit é limitar quantas vezes uma ação pode ser executada num intervalo de tempo. Por exemplo, permitir só 100 requisições por hora para um mesmo usuário, ou bloquear tentativas de login em excesso. A implementação muda de tecnologia para tecnologia, mas o conceito é sempre o mesmo: proteger o sistema de abuso, seja intencional ou não, e às vezes controlar o consumo de algum tipo de crédito (é o modelo que a maioria das APIs de LLM usa hoje).
Idempotência: evitar duplicação quando o usuário clica duas vezes
Imagina um usuário criando uma conta. Ele clica em "criar", a internet está lenta, ele clica de novo, e de novo. Sem nenhum tratamento, isso pode gerar três requisições POST idênticas e, consequentemente, três usuários iguais no banco.
Uma forma comum de evitar isso é gerar uma chave de idempotência para aquela operação específica (pode ser um hash a partir dos dados enviados mais um timestamp, ou uma sessão criada previamente no servidor, como acontece em fluxos de pagamento). Antes de criar o recurso, o servidor verifica se aquela chave já foi usada. Se já foi, ele não cria de novo, só devolve o resultado da primeira vez.
Vale notar que PUT e PATCH, por natureza, já costumam ser idempotentes: se eu mando dez requisições trocando o nome de um usuário para "Pedro", o resultado final é sempre o mesmo, o nome vira "Pedro" uma vez só. O problema de duplicação aparece sobretudo em POST, quando cada requisição cria um recurso novo.
E o GraphQL?
Vale um comentário rápido: em GraphQL, praticamente toda requisição, inclusive as que só buscam dado, usa o método POST. É uma dinâmica bem diferente da do REST, onde o método já entrega parte da intenção da requisição.
Resumindo
Método certo para cada ação, separação clara entre path, query e body, status code condizente com o que aconteceu, documentação, rate limit e idempotência onde faz sentido: isso já coloca a sua API acima da média. Muita API por aí não segue nem metade desses pontos. Dominando isso, você já sai na frente da maioria dos júniors.
Leia também