O desenvolvedor Wesley Beary, membro da equipe de desenvolvimento de API da Heroku, elaborou um guia com boas práticas para criação de APIs HTTP + JSON.
O guia inicia com um conjunto de recomendações genéricas, como:
- Exigir que todas as chamadas para a API utilizem o protocolo TLS. Caso contrário, responda a requisição com o retorno "403 Forbidden";
- Versionar a API. Utilize o cabeçalho "Accept" para especificar a versão e também exigir de alguma forma que a aplicação especifique qual versão da API está chamando, ao invés de somente depender da versão padrão;
- Utilizar o cabeçalho ETag para suportar cache dos resultados;
- Identificar o campo X-Request-ID no cabeçalho de resposta; útil para fins de log ou depuração;
- Utilizar os campos Range, Content-Range, Next-Range no cabeçalho de resposta para lidar com grandes respostas.
A seguir temos um exemplo de requisição e resposta:
Requisição:
GET /apps HTTP/1.1 Host: api.heroku.com User-Agent: … Range: name.; order=desc; Accept: application/vnd.heroku+json; version=3
Resposta
HTTP/1.1 200 OK Accept-Ranges: id Content-Range: id 01234567-89ab-cdef-0123-456789abcdef..01234567-89ab-cdef-0123-456789abcdef; max=200 ETag: "0123456789abcdef0123456789abcdef" Last-Modified: Sun, 01 Jan 2012 12:00:00 GMT RateLimit-Remaining: 2400
Referente a resposta das requisições, o guia menciona:
- Retorne o código de status apropriado, como:
- Código 200 (OK) para requisições GET respondidas com êxito ou síncronos DELETE ou PATCH;
- Código 201 (Created) para requisições POST respondidas com êxito;
- …
- Código 401 (Unauthorized Call) para chamadas não autorizadas;
- Código 429 (Too many requests);
- etc.
- Retorne o recurso completo quando possível;
- Aceite JSON nas requisições para existir simetria com as respostas JSON;
- Utilize uri-path em formatos consistentes. O nome dos recursos deve estar nomeado no plural, a menos que se refira a uma coisa única;
- Utilize minúsculo na nomenclatura dos paths e atributos para acompanhar o padrão dos nomes de host;
- Permita que alguns recursos, tal como nomes da aplicação, serem endereçados pelos seus nomes, além do UUID (Universally unique identifier);
- Quando possível, limite a profundidade dos recursos aninhados alocando eles o mais próximo da raiz.
Além disso, Beary também sugere outras regras nas respostas:
- Utilize globalmente IDs único para cada recurso;
- Forneça campos padrões para cada recurso como, por exemplo: os campo "criadoEm", "alteradoEm";
- Para timestamps, utilize UTC no formato ISO8601;
- Aninhe relações com chave estrangeira para incluir mais detalhes nos recursos sem ter que alterar a estrutura da resposta;
- Forneça erros detalhados, incluindo um identificador para outra aplicação, como também uma mensagem para usuário ou desenvolvedor. E se possível, uma mensagem de erro com URL apontando para documentação;
- Estabeleça uma taxa de requisições (rate limit) e informe ao cliente a quantidade de requisições ainda disponíveis em cada resposta, utilizando um cabeçalho como por exemplo: RateLimit-Remaining;
- A resposta JSON deve ser minificada em todas as respostas.
Exemplo de resposta de erro:
HTTP/1.1 429 Too Many Requests { "id": "rate_limit", "message": "Sua conta atingiu a taxa de requisições permitida pela API.\nPor favor, aguarde alguns minutos antes de fazer novas requisições.", "url": "https://devcenter.heroku.com/articles/platform-api-reference#rate-limits" }
O guia inclui mais conselhos relacionados, como:
- Fornecer um esquema JSON no formato que possa ser legivél para uma maquina;
- Incluir uma documentação detalhada para desenvolvedores;
- Permitir que os desenvolvedores testem a API;
- APIs deveriam ser classificadas de acordo com o status de sua respectiva estabilidade: protótipo/desenvolvimento/produção.
O InfoQ.com entrevistou Wesley Beary para perguntar como surgiu o guia com as boas rpáticas e explicar alguns pontos.
InfoQ.com: Já havia algum guia desde o inicio?
Beary: Não, apenas tinha algumas ideias sobre como deveria ser o guia de estilo, mas no início essas ideias estavam simples e ainda não havia sido publicado em nenhum lugar. E a partir do meu trabalho publicado no github.com/fog/fog e outras experiências que tive consumindo diferentes APIs, foi a forma que eu encontrei para formar minhas opiniões.
InfoQ.com: Se não tinha o guia de estilo no início, como se desenvolvia a API? Através de várias iterações?
Beary: Na falta de uma guia formal, era definitivamente necessário um processo de várias iterações. Infelizmente, além de ser algo lento, o processo de iteração não era escalável. Isso me deixava como uma barreira ao adicionar novas funcionalidades. Então construímos o guia para compartilhar o processo de criação de regras. Nossa equipe de desenvolvimento continua a iterar, mas frequentemente as definições já estavam no próprio guia. Além disso, há contínuas discussões na página do projeto (github.com/interagent/http-api-design/issues) para esclarecer e aprimorar o documento, assim aprendemos mais e outros desenvolvedores também passaram a utilizar o guia.
InfoQ.com: Por que é recomendado retornar o recurso inteiro durante uma operação de PUT ou DELETE?
Beary: Casos excepcionais são os inimigos das interfaces e guia de estilos. Algumas vezes, o recurso inteiro será desejado e não ter é muitas vezes apenas por questões de otimização. Acredito que ter como opção o comportamento completo pode ser algo bom em muitos casos, mas ter um padrão fácil e útil é o mais desejado. E como havia dito, há diversas discussões na página do projeto, sobre este ponto em particular, há uma discusso mais aprofundada sobre os prós e contras em https://github.com/interagent/http-api-design/issues/9.
InfoQ.com: Por que utiliza somente hora UTC no formato ISO8601?
Beary: Similar ao processo de retornar sempre o recurso inteiro, ter uma regra consistente, sem exceções, simplifica o que se esta fazendo e reduz o número de decisões que durante o desenvolvimento. Além disso, "time zones" são complexas. E para reduzir problemas, decidimos escolher um timezone especifico (UTC) e formatar tudo baseado nisso.