O que mudou em relação a versão anterior?
03/09/2019 - Exemplo de aplicação de filtro simples em estruturas com entidades aninhadas.
Índice
- 1 O que mudou em relação a versão anterior?
- 2 Introdução
- 3 Termos e Nomenclaturas
- 4 Comitê
- 5 Estrutura de URLs
- 5.1 Agrupamento
- 6 Formatos de Data
- 7 Coleções
- 8 Métodos suportados
- 9 Como escolher o método adequado
- 10 Padrões de cabeçalhos de requisição
- 11 Padrões de cabeçalhos de respostas
- 12 Cabeçalhos customizados
- 13 Formato das mensagens de resposta
- 13.1 Mensagens de erro
- 13.2 Mensagens de sucesso
- 14 Código 4xx versus 5xx
- 15 Filtro de campos da resposta
- 16 Parâmetros expansíveis
- 17 Requisições Assíncronas
- 17.1 Processando
- 17.2 Finalizada – Erro
- 17.3 Finalizada – Sucesso
- 18 Padrão HATEOAS
- 19 Tipos de Conteúdo
- 20 Versionamento
- 21 Documentação
Introdução
Este documento define os padrões que devem ser adotados durante a implementação de novas APIs publicas ou privadas na plataforma TOTVS incluindo:
- Definir práticas e padrões consistentes para todos os endpoints das APIs da TOTVS;
- Garantir a utilização mais próxima possível das boas práticas estipuladas pelos padrões REST/HTTP;
- Tornar os serviços da TOTVS acessíveis através de APIs facilmente compreendidas e documentadas para desenvolvedores e consumidores.
Importante!
Atente-se para esta documentação. Para garantir um bom desenvolvimento de APIs publicas ou privadas, é imprescindível que os passos a seguir sejam respeitados.
Termos e Nomenclaturas
Os termos deve, não deve, requerido, pode, não pode, recomendado, opcional devem ser interpretados como descritos no padrão RFC-2119.
Corpo de mensagem e Query String
Cliente: Qualquer aplicativo que faça uma requisição para um endpoint.
Mensagem: Conteúdo enviado no corpo de uma requisição ou resposta do servidor.
Endpoint: Representa um método ou entidade que pode ser acessado através de uma requisição ao servidor.
Verbo: Tipo de requisição usada para acessar um endpoint (GET, POST, PUT, HEAD, etc).
API: Grupo de endpoints.
APIs Privadas são todas as APIs acessíveis apenas pelos times.
APIs Publicas são todas as APIs que podem ser acessadas por clientes externos aos times de desenvolvimento.
ISO: Internation Organization for Standardization, organismo responsável por diversas padronizações em nível mundial. A ABNT tem a mesma função, para padrões em âmbito nacional.
UTC: Universal Time Coordinated, padrão de registro de tempo para uso em nível mundial. Várias normas técnicas, como a ISO-8601 e a RFC-3339 fazem referência a este padrão.
Comitê
Criamos um comitê interno, formado com um integrante de cada segmento, para discutir e garantir a execução dos padrões definidos neste documento.
Cada um dos membros deve obrigatoriamente ser incluído no pull request de novas APIs publicas e cada um deles é responsável por garantir a correta disseminação e implementação dentro de seu próprio time das APIs privadas.
Segmento | Membro |
---|---|
Fluig | |
Integração | |
RM | |
Protheus | |
Datasul |
Estrutura de URLs
Como regra geral seguimos os passos abaixo sempre que precisarmos criar um novo recurso:
- Determinar o tipo de recurso que está sendo exposto pelo endpoint;
- Determinar o relacionamento do recurso que está sendo criado e os recursos já existentes;
- Decidir o caminho e o nome do recurso com base no seu tipo e o seu relacionamento com os outros recursos;
- Anexar os métodos mínimos para o correto funcionamento do endpoint.
Os caminhos definidos para cada endpoint devem ser de fácil leitura e significativos para o cliente para facilitar a sua descoberta e adoção. Os pontos abaixo devem ser considerados ao criar uma URL:
- Ao referenciar uma entidade na URL ela deve estar no plural. Ex. /users ao invés de /user;
- Evite caminhos com mais de 3 parâmetros (além da versão da API), pois isso dificulta a leitura e normalmente indica um problema arquitetural;
- O caminho deve apontar para um recurso e não para uma ação sobre a entidade, use os verbos HTTP para representar uma ação. Nos casos onde não exista um verbo apropriado a ação pode ser informada como parâmetro no caminho da URL ou na URL.
- Ao utilizar Query Param com nome composto, sugerimos a utilização do padrão camelCase ou tudo minúsculo separado por hífen.
- Sugerimos utilizar o padrão camelCase ou tudo minúsculo separado por hífen para nome de recursos que contenham mais de uma palavra. Exemplos: orderItem, customerVendor, customer-vendor etc.
- Uma vez definido o padrão no XSD ou JsonSchema, obrigatoriamente deve seguir o contratado.
Exemplo de URLs amigáveis
- Entidade apresentada no plural e uso correto do parâmetro na url.
- https://totvs.com/api/fdn/v1/users/{id}
- Ações sobre entidades no caminho da url e como parâmetro da url.
- https://totvs.com/api/bpm/v1/workflows/{id}?action=execute
- https://totvs.com/api/bpm/v1/workflows/{id}/send
- Relacionando entidades diretamente no caminho
- https://totvs.com/api/fdn/v1/users/{alias}/communities
- https://totvs.com/api/social/v1/communities/{id}/users
- URL com suporte a campos expansíveis
- https://totvs.com/api/fdn/v1/users/{alias}?expand=prop1.subprop,prop2
- Ordenando e paginando coleções
- https://totvs.com/api/fdn/v1/users?order=name,-age&page=2&pageSize=20
- URL com Query Param e nome composto:
- https://totvs.com/api/fdn/v1/users/?firstName=joao
Exemplo de URLS NÃO amigáveis e que deve ser evitadas
- Redundância na definição da url
- https://totvs.com/api/communty/listCommunities
- Ordenação especificada na url
- https://totvs.com/api/communty/listCommunitiesWithRelevance
- Verbos definidos na url ao invés de usar o verbo HTTP e falta de pluralização no substantivo
- https://totvs.com/api/correlationquestion/create
- https://totvs.com/api/user/create
- https://totvs.com/api/user/delete
- Identificador da entidade definido como parâmetro e não como caminho (na url)
- https://totvs.com/api/document/permissions?documentId={id}
- https://totvs.com/api/document/permissions?documentId={id}
Apesar de o padrão definido no documento RFC 7230 da especificação do HTTP 1.1 não definir um tamanho máximo para a url, nenhum endpoint deve ser identificado com uma url maior que 2000 caracteres para garantir que todos os navegadores modernos sejam suportados.
Fique atento!
Mais informações sobre a escolha do valor máximo de caracteres pode ser consultada em:
https://blogs.msdn.microsoft.com/ieinternals/2014/08/13/url-length-limits/
Agrupamento
Consideramos uma API o agrupamento de endpoints que façam parte da mesma unidade de negócio, criar um novo grupo de endpoints implica em preocupações como versionamento, disponibilidade e documentação. Usamos como base a estrutura "{protocolo}://{host}/{api}/{agrupador}/{dominio}/{versao}/{recurso}". Ex: https://fluig.totvs.com/api/ecm/security/v1/users. A partir deste ponto deve-se considerar como regra básica a complexidade necessária para descobrir estes endpoints, ou seja, o agrupamento deve facilitar e não complicar a descoberta dos serviços.
Importante entender a diferença entre o agrupador e o domínio no nome da API.
O agrupador, é geralmente definido o segmento de atuação da API, é uma maneira de facilitar a localização das API, EX.: Financeiro, Contábil.
Já o domínio é uma subdivisão especifica de determinado módulo, criando assim o escopo de uma determinada API, separando somente seus endpoints, facilitando o uso de ferramentas de gerenciamento de API EX.: Financeiro/ContasPagar, Financeiro/ContasReceber, Contábil/Lançamentos, Contábil/Balanço.
APIs escritas utilizando o protocolo de mensagem padronizada não devem definir o nome do produto na URL. A ideia de APIs padronizadas é que a mesma assinatura atenda diferentes produtos.
A assinatura de API padronizada segue a nomenclatura abaixo:
"{protocolo}://{host}/{api}/{agrupador}/{dominio}/{versao}/{recurso}"
Estrutura OAS (Open Api Specification 3.0.1):
openapi: 3.0.1 info: title: Estrutura de Url para API's description: 'Consideramos uma API o agrupamento de endpoints que façam parte da mesma unidade de negócio. Usamos como base a estrutura "{protocolo}://{host}/{api}/{agrupador}/{dominio}/{versao}/{recurso}"' version: '1.0' externalDocs: url: 'http://tdn.totvs.com/display/INT/Guia+de+implementacao+das+APIs+TOTVS' servers: - url: '{protocolo}://{host}/{api}/{agrupador}/{dominio}/{versao}/{recurso}' variables: protocolo: default: https enum: - http - https host: default: host api: default: api agrupador: default: agrupador dominio: default: dominio versao: default: versao recurso: default: recurso paths: {} components: {}
Ex:
GET http://totvs.com/api/fluig/ecm/v1/documents GET http://totvs.com/api/fluig/bpm/v1/workflows GET http://totvs.com/api/fluig/lms/v1/classes
Formatos de Data
Todos as transações de informação que suportam campos do tipo data/hora, tanto no corpo das mensagens quanto na URI, devem usar um dos seguintes formatos:
- Data e hora combinados (E8601DZw): yyyy-mm-ddThh:mm:ss+|-hh:mm
- Data (E8601DAw.): yyyy-mm-dd
- Hora (E8601TMw.d): hh:mm:ss.ffffff
Detalhes sobre o padrão utilizado acima podem ser encontrados nos documentos ISO-8601 e uma tabela com os formatos E8601DZw, E8601DAw. e E8601TMw.d podem ser encontrados no documento Extended Notations.
Coleções
Ordenação
Todos os endpoints de coleção devem suportar ordenação. A definição de como a lista deve ser ordenada é definida no parâmetro de query order respeitando as seguintes regras:
- O parâmetro de query da URL deve ter o nome order;
- O valor do parâmetro order é uma lista de campos, separada por virgula (,) opcionalmente precedida pelo sinal de subtração (-);
- A lista deve respeitar a ordem dos campos como descrito na url para fazer a ordenação;
- Campos precedidos por um sinal de subtração (-) devem ser ordenados de forma decrescente;
- Campos que omitirem o sinal (subtração) devem ser ordenados de forma crescente.
Por exemplo, a seguinte requisição deve retornar a lista de usuários ordenados pelo nome de forma crescente e então pela idade de forma decrescente e então pelo sobrenome de forma crescente:
GET https://totvs.com.br/api/fluig/fdn/v1/users?order=name,-age,surname
Ordenação na URL
Se um tipo de ordenação é usado constantemente ou é complexa demais para definir como parâmetros de query pode-se considerar criar um endpoint para representá-la desde que essa siga as boas praticas na definição da URI.
Filtros
As coleções podem suportar filtro e devem usar as técnicas abaixo de acordo com a complexidade exigida pela função:
Filtros simples
Filtros simples devem usar parâmetros de query no formato propriedade=valor e as propriedades suportadas, bem como a forma como serão interpretadas, devem estar listadas na documentação do endpoint. Ex:
GET https://totvs.com.br/api/fluig/fdn/v1/users?name=john&surname=doe
Opcionalmente, em endpoints que tenham estrutura com entidades aninhadas, a propriedade usada para filtrar os resultados deve usar o formato propriedade_pai.propriedade_filho.
Não há limite para os níveis que se pode referenciar, mas deve-se levar em consideração o quão complexa pode ser a implementação. Quando houver limitação quanto a quantidade de níveis referenciados, importante documentar na API.
Exemplo de uso do filtro com propriedades aninhadas:
GET https://totvs.com.br/api/fluig/fdn/v1/users?communities.name="Vendas"
No exemplo acima, serão retornados todos os usuários (users) cuja propriedade name (que está contida na propriedade communities da entidade users) seja igual a "Vendas".
Filtros complexos (opcional)
Filtros complexos e que forneçam uma linguagem de filtro devem seguir o padrão definido para os filtros no documento ODATA na versão 4.
Exemplo de filtros no padrão odata:
Operator | Description | Example |
Logical Operators | ||
eq | Equal | /Suppliers?$filter=Address/City eq 'Redmond' |
ne | Not equal | /Suppliers?$filter=Address/City ne 'London' |
gt | Greater than | /Products?$filter=Price gt 20 |
ge | Greater than or equal | /Products?$filter=Price ge 10 |
lt | Less than | /Products?$filter=Price lt 20 |
le | Less than or equal | /Products?$filter=Price le 100 |
and | Logical and | /Products?$filter=Price le 200 and Price gt 3.5 |
or | Logical or | /Products?$filter=Price le 3.5 or Price gt 200 |
not | Logical negation | /Products?$filter=not endswith(Description,'milk') |
Grouping Operators | ||
( ) | grouping | /Products?$filter=(Price lt 5) |
Filtros de data Modificação (opcional)
Opcionalmente, a api pode disponibilizar o filtro especial "datemodified" para retornar registros alterados a partir de uma determinada data.
O valor desse filtro deve seguir o definido em Formatos de data.
Exemplo:
GET https://totvs.com.br/api/fluig/fdn/v1/users?datemodified='2018-01-01'
ou combinar com o padrão oData para retornar apenas registros alterados em determinada data:
GET https://totvs.com.br/api/fluig/fdn/v1/users?$filter=datemodified ge '2018-01-01' and datemodified le '2018-01-31'
Paginação
Todos os endpoints de coleção devem suportar paginação. A definição de como a coleção deve ser paginada é definida pelos parâmetros de url page e pageSize respeitando as seguintes regras:
- Os parâmetros da url devem ter os nomes page e pageSize;
- O valor do parâmetro page deve ser um valor numérico (maior que zero) representando a página solicitada;
- O parâmetro page é opcional e na sua ausência deve ser considerado o valor
- O valor do parâmetro pageSize deve ser um valor numérico (maior que zero) representando o total de registros retornados na consulta;
- O parâmetro pageSize é opcional e na sua ausência deve ser usado o valor padrão definido pela linha de negócio do endpoint (20 por exemplo). Não devem ser retornados todos os registros da consulta;
- Os parâmetros de paginação devem obedecer a semântica de multiplicador, ou seja, se o cliente solicitou page=2 com um pageSize=20 deve-se retornar os registros de 21 até 40;
- A resposta de uma requisição com paginação deve retornar um campo indicando se existe uma próxima página disponível conforme descrito na mensagem de sucesso de lista e esse campo deve ter o nome hasNext.
Por exemplo, a seguinte requisição deve retornar a quarta página de registros (dos registros 31 à 40 inclusive) de usuários:
GET https://totvs.com.br/api/fluig/fdn/v1/users?page=4&pageSize=10
Métodos suportados
Endpoints devem usar os métodos HTTP adequados e devem respeitar a idempotência da operação.
Na tabela abaixo serão listados os métodos que devem ser suportados. Nem todos os endpoints devem suportar todos os métodos, mas os que o fizerem devem respeitar a utilização conforme descrita.
Método | Descrição | Idempotente |
---|---|---|
GET | Retorna o valor corrente do objeto. | Sim |
PUT | Sobrescreve o objeto quando aplicável. Por exemplo: O cliente gostaria de sobrescrever o usuário com novos valores: PUT http://totvs.com/api/users/10 { name: "John", age: 25, ... } Importante Caso o cliente não informe alguma propriedade para ser atualizada, está deve ser considerada nula. Está informação deve estar clara na documentação do método para que o cliente não há utilize inadvertidamente, ou pode-se optar por não implementá-la. | Sim |
DELETE | Exclui o objeto. | Sim |
POST | Cria um novo objeto ou submete um comando ao objeto. | Não |
HEAD | Retorna os metadados da requisição em casos em que o cliente não precisa do corpo das requisições do tipo GET. | Sim |
PATCH | Também usado para atualizar uma entidade, mas diferente do PUT, recebe em seu corpo uma série de instruções ou o estado no qual o cliente gostaria que a entidade estivesse no final da operação. Deve ser tratado de forma atômica, ou seja, ou todas as instruções foram completadas com sucesso ou deve retornar erro ao cliente. PATH pode causar efeitos colaterais e portanto não é considerado seguro ou idempotente. PATCH http://totvs.com/api/fluig/fdn/v1/users/10 [ { "op": "replace", "path": "/name", "value": "Bob" } ] O servidor deverá implementar o serviço de modo que apenas o nome do usuário seja atualizado e todas as outras propriedades sejam mantidas. | Não |
OPTIONS | Deve retornar pelo menos o campo Allow no cabeçalho da resposta listando os verbos suportados pelo endpoint. | Sim |
Corpo de mensagem e Query String
Métodos DELETE, GET, HEAD e OPTIONS não deve ser utilizado corpo na mensagem e sim utilizar query string .
Em métodos POST evitar a utilização de path param e utilizar corpo na mensagem a fim de manter as boas práticas de desenvolvimento.
Campo chave (id) dos verbos GET, PUT e DELETE
O valor passado como parâmetro de URL (path param) dos verbos GET, PUT e DELETE deve representar o identificador único do registro dentro do contexto do endpoint. Ou seja, baseado no valor (id) passado, o provedor de informação deve ser capaz de identificar de forma inequívoca o registro dentro da base de dados. Caso necessário, é possível concatenar diversos campos para compor o identificador.
POST x PUT x PATCH
Deve-se atentar aos códigos de retorno para os tipos de operações definidos para usar estes métodos principalmente nos casos definidos abaixo:
Verbo | Usado para | Tipo de operação | Código de sucesso | Detalhes |
---|---|---|---|---|
POST | Criar uma entidade | Síncrona | 201 | Além do código de sucesso deve retornar a nova entidade criada. |
PATCH ou PUT | Atualizar uma entidade | Síncrona | 200 | Além do código de sucesso deve retornar a nova entidade atualizada. |
POST, PUT | Criar ou atualizar entidade | Assíncrona | 202 | O código 202 informa ao cliente que o serviço aceitou a requisição para processamento e que isso pode levar um tempo para concluir. Nesse caso o endpoint deve retornar o campo Location no cabeçalho da resposta apontando para um recurso temporário que permita o acompanhamento do status da requisição. Por exemplo, considere o endpoint de cadastro assíncrono de usuários: POST https://totvs.com/api/fluig/fdn/v1/users { ... } DEVE retornar como resposta: 202 Accepted Location: https://totvs.com/api/fluig/fdn/v1/queue/10 |
Como escolher o método adequado
Abaixo descrevemos algumas situações e o método adequado a ser usado.
GET
- Quero retornar uma lista de usuários;
- Quero retornar o usuário com o id x;
- Quero retornar todas as permissões do usuário x;
- Quero retornar todos os usuário maiores de idade;
PUT
- Quero sobrescrever o usuário atual na base de dados usando os dados informados na requisição;
- Quero alterar o estado do usuário para ativo independente de outras regras;
PATCH
- Quero alterar o estado do usuário para inativo validando uma série de regras no servidor;
- Quero alterar uma propriedade do usuário e para faze-lo o servidor irá alterar também propriedades de outras entidades;
- Quero alterar a data de vencimento de uma conta mas apenas se for nos primeiros 15 dias do mês;
POST
- Quero adicionar um novo usuário;
- Quero executar uma ação ou serie de operações no usuário;
Padrões de cabeçalhos de requisição
A utilização destes cabeçalhos não é obrigatória para todos os endpoints, mas os que os utilizarem devem obedecer as regras descritas abaixo.
Header | Tipo | Descrição |
---|---|---|
Authorization | literal | Cabeçalho usado para autorização das requisições |
Date | data | Data e hora da requisição com base no calendário do cliente no formato estipulado em RFC 5322 |
Accept | enumerador de tipo de conteúdo | O tipo de conteúdo esperado para a resposta como por exemplo:
De acordo com os padrões HTTP, este valor é apenas uma dica para o servidor e as respostas podem retornar valores em formatos diferentes dos informados. |
Accept-Encoding | Gzip, deflate | Endpoints devem suportar codificação GZIP e DEFLATE por padrão exceto em casos em que isso implique na performance. |
Accept-Language | "en", "es", "pt" | Especifica o idioma preferencial da resposta. Os endpoints devem suportar e respeitar estes valores em casos em que uma mensagem é retornada ao usuário. Caso o idioma informado não seja suportado, deve-se retornar ao idioma padrão (pt). |
Content-Type | tipo de conteúdo | Tipo do conteúdo sendo passado na requisição. O endpoint deve respeitar esta informação na hora de interpretar a informação passada pelo cliente ou retornar um erro apropriado caso o valor não for suportado. |
Padrões de cabeçalhos de respostas
Endpoints devem retornar estes cabeçalhos em todas as repostas, a menos que explicitamente citado o contrário na coluna obrigatório.
Cabeçalho | Obrigatório | Descrição |
---|---|---|
Date | Em todas as respostas | Hora em que a resposta foi processada baseada no calendário do servidor no formato estipulado em RFC 5322. Ex: Wed, 24 Aug 2016 18:41:30 GMT . |
Content-Type | Em todas as respostas | O tipo de conteúdo enviado do endpoint para o servidor. |
Content-Encoding | Em todas as respostas | GZIP ou DEFLATE como for apropriado. |
Cabeçalhos customizados
Cabeçalhos customizados não devem ser utilizados para representar qualquer estado, valor ou ação sobre a entidade representada pela url. Isso quer dizer que cabeçalhos não devem ter relação com o cliente do endpoint (mobile, desktop, etc), url, corpo da mensagem, corpo da resposta ou parâmetros da url. O protocolo HTTP oferece uma série de cabeçalhos para quase todas as situações e estas sempre tem precedência a um cabeçalho customizado.
Nos casos extremos onde não houver outra maneira de representar a informação, pode-se optar por criar um cabeçalho que deve estar descrito na documentação do endpoint e este deve seguir o padrão X-[nome do produto]-[Nome do cabeçalho]
Formato das mensagens de resposta
O código HTTP das mensagens de resposta devem ser implementados de acordo com os padrões definidos no protocolo. Para mais detalhes, acesse o link abaixo e leia a especificação completa:
https://developer.mozilla.org/pt-BR/docs/Web/HTTP/Status
JSON deve ser o formato padrão para mensagens de resposta. Apenas nos casos onde se justificar, outro tipo de mídia pode ser usado. Por exemplo, numa API que retorne uma imagem ou um documento PDF.
Tipos de dados JSON
Os tipos de dados e a notação para os campos da mensagem JSON deve seguir os padrões descritos no documento ECMA-404.
Importante
Os campos do JSON que representam data devem usar o formato definido no documento ISO-8601 no formato EXTENDIDO.
Mensagens de erro
Para todas as mensagens que representam um erro (códigos HTTP 4xx e 5xx) deve-se retornar obrigatoriamente os campos a seguir:
{ code: "Código identificador do erro", message: "Literal no idioma da requisição descrevendo o erro para o cliente", detailedMessage: "Mensagem técnica e mais detalhada do erro" }
- O campo code deve identificar unicamente o erro na documentação da API;
Todos os códigos de erro devem estar mapeados e listados na documentação da API.
- O campo message deve descrever o erro de forma não técnica e pode ser usado pelos clientes para exibição ao usuário. O texto deste campo deve respeitar o idioma usado na requisição (definido pelo cabeçalho Accept-Language ou o padrão caso este não seja especificado). Além disso leve em consideração:
- Não assuma que o usuário é um especialista no uso da sua API. Usuários podem incluir desenvolvedores, profissionais de TI, administradores e usuários finais;
- Não assuma que o usuário saiba qualquer coisa sobre a implementação da função sendo exposta pelo endpoint;
- Quando possível a mensagem deve ser construida de forma que um usuário técnico possa responder ao erro e corrigi-lo;
- Mantenha a mensagem sucinta e, se necessário, inclua um link para a documentação de como corrigi-la.
- O campo detailedMessage deve descrever o erro de forma mais técnica e pode conter referências que ajudem na correção / localização do erro no contexto do servidor.
Opcionalmente pode-se retornar os campos:
- helpUrl: link para a documentação do erro;
- details: Uma lista de objetos de erro (recursiva) com mais detalhes sobre o erro principal.
Exemplo:
{ code: "Código identificador do erro", message: "Literal no idioma da requisição descrevendo o erro para o cliente", detailedMessage: "Mensagem técnica e mais detalhada do erro", helpUrl: "link para a documentação do error", details [ { code: "Código identificador do erro", message: "Literal no idioma da requisição descrevendo o erro para o cliente", detailedMessage: "Mensagem técnica e mais detalhada do erro" }, { code: "Código identificador do erro", message: "Literal no idioma da requisição descrevendo o erro para o cliente", detailedMessage: "Mensagem técnica e mais detalhada do erro" }, { code: "Código identificador do erro", message: "Literal no idioma da requisição descrevendo o erro para o cliente", detailedMessage: "Mensagem técnica e mais detalhada do erro" } ] }
Em alguns casos se faz necessário retornar mais campos do que os estipulados acima. Nestes casos a informação deve ser considerada opcional do ponto de vista do cliente, ou seja, o cliente não deve depender dela para o tratamento do erro.
Mensagens de sucesso
Mensagens de sucesso (código http 2xx) devem retornar diretamente a entidade que representa o objeto resultante da operação do endpoint. Ex:
GET http://totvs.com.br/api/fluig/fdn/v1/users/10 { _expandables: ["permissions","communities","detailedInformation"], id: 10, name: "John", surname: "Doe", age: 25, country: "US" }
Opcionalmente, o atributo _messages pode ser incluído no objeto retornado para fornecer alguma informação complementar ao processamento realizado (mensagens de aviso, de negócio, etc.). O formato do objeto de mensagem segue o padrão anteriormente descrito, para mensagens de erro.
GET http://totvs.com.br/api/fluig/fdn/v1/users/10 { _expandables: ["permissions","communities","detailedInformation"], id: 10, name: "John", surname: "Doe", age: 25, country: "US" _messages: [{ code: "INFO", message: "Esta é uma mensagem informativa", detailedMessage: "Mais detalhes sobre esta mensagem podem ser vistos aqui." }] }
Mensagens de sucesso para coleções
Nos casos em que o resultado da operação do endpoint represente uma coleção, os itens devem estar agrupados em um objeto com as propriedades hasNext, indicando se existe uma próxima página com mais registros para aquela coleção e items que representam a lista de itens retornados.
{ hasNext: true, items: [ {}, {}, ... ] }
Para o retorno de coleções, também é possível incluir o atributo _messages, conforme segue:
{ hasNext: true, items: [ {}, {}, ... ], _messages: [{ code: "INFO", message: "Uma mensagem informativa.", detailedMessage: "Detalhes relativos a mensagem." }] }
A estrutura acima é a mínima esperada para respostas de coleção. Entretanto, pode-se retornar mais campos se for necessário desde que a estrutura básica se mantenha. Por exemplo:
{ totvs_transaction_id: 1, total: 100, hasNext: true, items: [ {}, {}, ... ] }
Observe que na estrutura acima, os atributos totvs_transaction_id e total foram adicionados ao objeto de resposta, enquanto os atributos hasNext e items foram mantidos.
Mensagens de sucesso DELETE
Por padrão, nas mensagens de DELETE, a resposta deve ser enviada com HTTP Code 204 (No content) e sem corpo no retorno.
Caso a regra de negócio necessitar de um retorno com conteúdo, este deve vir com HTTP Code 200 (OK) e com a entidade excluída sem expandir os sub-níveis.
Código 4xx versus 5xx
Dividimos os erros em duas categorias: Erros de negócio ou requisição e Erros não esperados.
Erros de negócio ou requisição
São aqueles causados por dados inválidos nas requisições ou intencionalmente lançados do endpoint para o cliente. Todos os erros deste tipo devem obrigatoriamente retornar um código HTTP da família 4xx. Ex:
O cliente chamou um endpoint mas não estava devidamente autenticado. Deve ser retornado o código 401 - Unauthorized;
O cliente chamou um endpoint mas mesmo estando autenticado não possui as permissões necessárias para efetuar a operação. Deve retornar o código 403 - Forbidden;
O cliente chamou o endpoint para buscar um usuário (Ex: /users/{id}) mas o usuário não existe. Deve ser retornado o código de erro 404 - Not found;
O cliente chamou o endpoint para efetuar alguma operação mas por alguma regra de negócio a operação não pode ser concluída e não existe um código de erro HTTP apropriado para descrevê-lo. Deve ser retornado o código de erro 400 - Bad Request;
O cliente chamou o endpoint passando no cabeçalho que aceita como retorno xml (Content-Type: text/xml) mas o serviço só consegue retornar JSON. Deve retornar o erro HTTP 406 - Not Acceptable.
O cliente chamou o endpoint para criar um usuário mas já existe um usuário com o mesmo código. Deve ser retornado o código de erro 409 - Conflict
Erros não esperados
São os erros não tratados ou não intencionais. Esse tipo de erro deve sempre retornar códigos HTTP da família 5xx. Ex:
O cliente chamou um endpoint mas o servidor não pode responder por que estava sobrecarregado. Deve retornar o código 503 - Service Unavailable;
O cliente chamou um endpoint para subir um documento mas não havia espaço em disco no servidor. Deve retornar o código 507 - Insufficiente Storage.
Filtro de campos da resposta
A APIs devem permitir que o cliente filtre os campos retornados nas entidades. Para isso ele deve passar o parâmetro de query com nome fields, que consiste de uma lista com o nome das propriedades JSON que serão retornadas. Os campos omitizados serão removidos do retorno (mesmo que o cliente tenha solicitado expandi-los).
Omitir o campo fields fara com que todos os campos sejam retornados.
Por exemplo, o cliente faz uma requisição para retornar os dados do cliente com id 10:
GET http://totvs.com.br/api/fluig/fdn/v1/users/10 { _expandables: ["permissions","communities","detailedInformation"], id: 10, name: "John", surname: "Doe", age: 25, country: "US" }
Caso os únicos campos interessantes para o cliente sejam o "name" e "age" ele pode reduzir a quantidade de dados usando o parâmetro fields. Ex:
GET http://totvs.com.br/api/fluig/fdn/v1/users/10?fields=name,age { name: "John", age: 25 }
O parâmetro fields deve ter precedência sobre o parâmetro expand. Isso que dizer que o campo não será retornado a menos que esteja na lista de campos do parâmetro fields mesmo que ele esteja presente na lista de campos a serem expandidos do parâmetro expand.
Apenas a entidade principal pode ser filtrada usando o campo fields. Sub entidades não são afetadas e serão retornadas sempre por completo.
Parâmetros expansíveis
Indicamos o uso de campos expansíveis para todos os endpoints que possuam subníveis, todavia sua aplicação deve ser precedida da análise da equipe de negócio e com vistas a usabilidade.
O campo _expandables é uma lista com o nome de cada uma das propriedades que podem ser passadas na url para que o endpoint inclua na resposta.
Verbos HTTP x parâmetros expansíveis
Por exemplo, a entidade usuário possui propriedades que apontam para suas permissões, comunidades e detalhes e portanto estas devem estar retraídas:
GET https://totvs.com/api/fluig/fdn/v1/users/10 { _expandables: ["permissions","communities","detailedInformation"], id: 10, name: "John", surname: "Doe", age: 25, country: "US" }
Caso o cliente queira expandir as propriedades, ele deve então fazer uma requisição informando o parâmetro expand na url e passando como valor uma lista separada por virgula (,) dos campos exatamente como descritos no campo _expandables.
Atenção
Apenas o primeiro nível das propriedades deve ser expandido, ou seja, se o objeto expandido tiver propriedades expansíveis elas não serão retornadas.
Por exemplo, no cenário em que o cliente gostaria de expandir as comunidades da entidade usuário:
GET https://totvs.com/api/fluig/fdn/v1/users/10?expand=communities { _expandables: ["permissions","communities","detailedInformation"], id: 10, name: "John", surname: "Doe", age: 25, country: "US", communities: [{ _expandables: ["permissions"], # refere-se ao atributo permissions de communities (2o nivel) name: "Vendas", photoUrl: "https://totvs.com/communities/1/photo" }, { _expandables: ["permissions"], # refere-se ao atributo permissions de communities name: "Outra comunidade", photoUrl: "https://totvs.com/communities/2/photo" }] }
Note que as propriedades do objeto expandido não são retornadas por padrão. O cliente pode solicitar que as propriedades dos objetos sejam expandidas separando as sub-propriedades com ponto (.). Usando o exemplo acima, o usuário gostaria de expandir as permissões das comunidades da entidade usuário:
GET https://totvs.com/api/fluig/fdn/v1/users/10?expand=communities.permissions { _expandables: ["permissions","communities","detailedInformation"], id: 10, name: "John", surname: "Doe", age: 25, country: "US", communities: [{ name: "Vendas", photoUrl: "https://totvs.com/communities/1/photo", permissions: { isAdmin: true, canView: true } }, { name: "Outra comunidade", photoUrl: "https://totvs.com/communities/2/photo", permissions: { isAdmin: true, canView: true } }] }
limites
A API deve suportar a expansão de até dois níveis de propriedade de retorno, ex: prop1.prop2.prop3.
Nos casos em que o objeto tenha uma sub coleção é recomendável que o número máximo de registros dessa coleção não ultrapasse 20 registros. Se houver a necessidade de retornar mais itens, considerar criar um endpoint especifico para retornar a coleção com suporte a filtro, ordenação, etc.
Requisições Assíncronas
Ao realizar um POST, PUT ou DELETE em um recurso que executa seu processamento de maneira assíncrona, o mesmo DEVE retornar o código 202 Accepted, com cabeçalho location apontando para um recurso temporário que permita o acompanhamento do status da requisição.
Atenção
A implementação do modelo assíncrono não é obrigatório. A opção por este modelo deve basear no tempo e recurso necessário para o processamento de uma requisição.
Processando
Ao realizar o GET no recurso temporário, enquanto ainda estiver sendo processado, o mesmo DEVE retornar o código 200 e um JSON com a propriedade status definida como “pending”. Esse retorno PODE, e é uma boa prática, retornar a propriedade progress, informando o percentual de conclusão da operação solicitada.
Outra propriedade que PODE constar nesse retorno é a canCancel. Quando definida como “true”, significa que cancelar aquele processamento é permitido para o client. Quando inexistente, ou definida como “false”, o cancelamento não está permitido.
{ "status": "pending", "progress": "30%", "canCancel": true }
Para cancelar a execução do processamento, quando permitido, o client deve executar DELETE no recurso temporário.
Finalizada – Erro
Ao acessar o recurso temporário que finalizou seu processamento com erros, retorna o erro adequado, conforme definido em Mensagens de Erro e Código 4xx versus 5xx
Finalizada – Sucesso
Ao acessar o recurso temporário que finalizou seu processamento com sucesso, o mesmo DEVE retornar o código 303 (See Other) com o cabeçalho Location, apontando para endereço do real recurso criado.
A deleção do recurso temporário não é obrigatória e PODE ser feita através de DELETE do client, ou através do próprio server, após um tempo determinado para expirá-lo. Nesse segundo caso, ao tentar acessá-lo DEVE retornar 410 (Gone).
Padrão HATEOAS
Esse estilo de arquitetura permite usar links no conteúdo da resposta para que o cliente possa navegar dinamicamente para o recurso apropriado. Isso é conceitualmente o mesmo que um usuário da Web navegando pelas páginas e clicando nos hiperlinks apropriados para atingir uma meta final.
O padrão HATEOAS deve ser utilizado no retorno de entidades relacionadas a principal e que não são parte do recurso principal. Ex.
GET https://totvs.com/api/fluig/fdn/v1/users/10 { _expandables: ["permissions","communities","detailedInformation"], id: 10, name: "John", surname: "Doe", age: 25, country: "US", "links": [ { "rel": "communities", "href": "/fdn/v1/communities/5" }, { "rel": "permissions", "href": "/fdn/v1/permissions/30" } ] }
Tipos de Conteúdo
O formato padrão e recomendado de tipo de conteúdo, ou "Content-Type", a ser trafegado via APIs é "application/json".
Em alguns casos pode existir a necessidade de utilizar "application/xml" , por ex: quando é exigido por legislação. Nessa situação, as mesmas regras definidas nos tópicos anteriores continuam valendo, visto que estas estão relacionadas ao schema. (Se possível, evitar esse uso e sempre priorizar o JSON)
Outro cenário é o download e upload de arquivos binários: Não utilizamos os tipos "multipart/xxxx", e sim os mais específicos ao tipo do arquivo.
Exemplos:
"image/png" para o download ou upload de um arquivo com extensão ".png".
- "application/pdf" para o download ou upload de um arquivo com extensão ".pdf".
Clique aqui para lista completa dos tipos permitidos
Dica
Utilizar o cabeçalho "Content-Disposition"="attachment" na resposta de uma API faz com que o navegador faça o download do arquivo ao invés de renderizar o seu conteúdo
Versionamento
As APIs devem ser versionadas sempre que alguma alteração quebrar o contrato entre o usuário e a plataforma, a versão deve estar presente na URI e deve estar no forma v{major.minor}.
A versão major indica uma grande versão da API, ou seja, a API mudou significativamente em seu formato e comportamento.
A versão minor indica uma alteração que pode quebrar o código do cliente.
Por exemplo:
http://totvs.com/api/fluig/fdn/v1/users http://totvs.com/api/fluig/fdn/v1.5/users http://totvs.com/api/fluig/fdn/v2/users
Duas versões da API podem ser suportadas e neste caso deve existir uma politica de depreciação e esta deve estar documentada em um local acessível ao usuário final.
Politica de depreciação.
A politica de depreciação deve definir o ciclo de vida da versão da API estipulando um tempo limite em que ela receberá suporte e estará disponível. A politica pode ser única para cada área de negócio mas deve estar documentada e evidente para o usuário final.
Quando alterar a versão?
O numero da versão indica para o cliente quando alguma alteração pode "quebrar" o código escrito por ele. Deve-se tomar um cuidado especial no processo de versionamento para que o cliente esteja ciente dessas alterações e a frequência com que elas acontecem usando algum tipo de release notes ou documentação.
Alterações que devem gerar uma nova versão "major"
- URIs foram removidas ou renomeadas;
- Foram removidos campos do retorno de um endpoint;
- Foi removido o suporte a um método do endpoint (GET, PUT, POST, etc);
- Um novo parâmetro obrigatório é exigido para o correto funcionamento do endpoint;
- Um novo endpoint foi adicionado a API.
Alterações que NÃO devem gerar uma nova versão "major"
- Um novo formato de retorno é suportado pelo endpoint;
- Novas propriedades são retornadas na entidade de retorno do endpoint;
- Novos parâmetros opcionais podem ser passados para o endpoint.
APIs de mensagem padronizada
Documentação
Todas os métodos, parâmetros, códigos de erro e mensagens de requisição e retorno da API publica devem estar documentadas na página de documentação. Além disso deve ser gerado um documento OpenAPI com as definições da API.