RESTful: Boas práticas para design de API
Tags: webdev, api, rest, restfull
Published: 2023-03-17 00:18:08

Representational State Transfer (REST) é um estilo de arquitetura de software para sistemas distribuídos, amplamente utilizado na construção de APIs para a Web.

RESTful é um termo usado para descrever APIs da Web que aderem a esses princípios de design REST. Uma API da Web RESTful é baseada em recursos, que podem ser identificados por meio de URIs exclusivos. As ações que podem ser realizadas em um recurso são expressas por meio de métodos HTTP, como GET, POST, PUT e DELETE.

Esses princípios de design ajudam a tornar as APIs da Web mais simples, escaláveis, flexíveis e fáceis de entender e usar, permitindo a criação de sistemas mais robustos e interoperáveis.

## Por que o design de API é tão importante?
As pessoas fazem essa pergunta com bastante frequência e, para respondê-la:

As APIs REST são a face de qualquer serviço e, portanto, devem:

1. Ser fáceis de entender, para que a integração seja simples
2. Ser bem documentadas, para que os comportamentos semânticos sejam entendidos (não apenas sintáticos)
3. Seguir padrões aceitos, como o HTTP

---

## Projetando e desenvolvendo APIs REST altamente úteis
Existem várias convenções que seguimos na Hashmap ao projetar nossas APIs REST, para garantir que atendamos às expectativas listadas acima para o desenvolvimento de aceleradores e nossos projetos de consultoria.

Essas convenções são as seguintes:

## Use substantivos na URI
As APIs REST devem ser projetadas para Recursos, que podem ser entidades ou serviços, etc., portanto, eles devem sempre ser substantivos. Por exemplo, em vez de `/createUsers`, use `/users`.

É importante considerar que os métodos HTTP precisam identificar suas rotas e ações de forma clara e entendível, vejamos os exemplos a seguir:

```
GET /users
POST /users
PUT /users/{id}
GET /users/{id}
DELETE /users/{id}
```
Não use verbos:

```
/getAllUsers
/createNewUsers 
/deleteAllRedUsers
```

---

## Plural ou Singular
Geralmente, preferimos usar plurais, mas não há uma regra rígida que impeça o uso do singular para o nome do recurso. A ideologia por trás do uso de plurais é:

Estamos operando em um recurso de uma coleção de recursos, então, para representar a coleção, usamos o plural.

Por exemplo, no caso de...

```
GET /users/123
```

o cliente está solicitando recuperar um recurso da coleção de um usuário com o id 123. Ao criar um recurso, queremos adicionar um recurso à coleção atual de recursos, então a API ficaria assim...

```
POST /users
```
_Não misture substantivos singulares e plurais. Mantenha a simplicidade e use apenas substantivos no plural para todos os recursos._

---

## Deixe o verbo HTTP definir a ação

De acordo com o ponto #1 acima, as APIs devem fornecer apenas substantivos para recursos e deixar os verbos HTTP (GET, POST, PUT, DELETE) definirem a ação a ser realizada em um recurso.

**POST:**  É utilizado para criar um novo registro no banco de dados.
**GET:** É utilizado para ler registros no banco de dados.
**PUT:** É utilizado para atualizar um registro no banco de dados.
**PATCH:** É utilizado para atualizar parte de um registro no banco de dados.
**DELETE:** É utilizado para deletar um registro no banco de dados.

---

## Não use os métodos seguros de forma inadequada (idempotência)

Os métodos seguros são métodos HTTP que retornam a mesma representação de recursos independentemente do número de vezes que são chamados pelo cliente. GET, HEAD, OPTIONS e TRACE são definidos como seguros, o que significa que são apenas destinados a recuperar dados e não devem alterar o estado de um recurso em um servidor. Não use GET para excluir conteúdo, por exemplo...

```
GET /users/123/delete
```
Não é que isso não possa ser implementado, mas a especificação HTTP é violada neste caso.

Use os métodos HTTP de acordo com a ação que precisa ser executada.

---

## Represente hierarquia de recursos através da URI

Se um recurso contiver sub-recursos, certifique-se de representá-lo na API para torná-lo mais explícito. Por exemplo, se um usuário possui itens e desejamos recuperar uma item específica por usuário, a API pode ser definida como GET `/users/123/posts/1`, o que recuperará o item com o ID 1 do usuário com o ID 123.

---

## Versione suas APIs

Versionar APIs sempre ajuda a garantir a compatibilidade retroativa de um serviço ao adicionar novos recursos ou atualizar funcionalidades existentes para novos clientes. Existem diferentes escolas de pensamento sobre como versionar sua API, mas a maioria delas se enquadra nas duas categorias abaixo:

**Headers:**

Existem 2 maneiras de especificar a versão nos headers:

**Header personalizado:**

Adicionar uma chave de header personalizado X-API-VERSION (ou qualquer outro header de escolha) pelo cliente pode ser usada por um serviço para rotear uma solicitação para o endpoint correto.

**Accept Header:**

Usando o header Accept para especificar sua versão, por exemplo:

```
Accept: application/vnd.service.v2+json
```

**URL:**

Incorpore a versão na URL, como por exemplo:

```
GET /v1/users
POST /v2/users
```

Embora ambos os métodos possam ser utilizados para versionar APIs, o método de URL oferece melhor descoberta de recursos ao olhar para a URL. É importante lembrar que a escolha do método de versionamento é uma decisão do desenvolvedor e ambas as abordagens têm suas vantagens e desvantagens.

---

## Retornar representação

Os métodos POST, PUT ou PATCH, usados para criar um recurso ou atualizar campos em um recurso, devem sempre retornar uma representação atualizada do recurso como resposta, juntamente com um código de status apropriado, conforme descrito nos pontos a seguir.

Se o POST for bem-sucedido ao adicionar um novo recurso, ele deve retornar o código de status HTTP 201, juntamente com a URI do novo recurso criado no cabeçalho Location (conforme especificado na especificação HTTP). Por exemplo:

```json
POST /users

Request Body:
{
  "nome": "Novo usuário",
  "idade": 30,
  "email": "novo_usuario@email.com",
  "cidade": "Rio de Janeiro"
}

Response:
Status Code: 201 Created
Location: /users/2
{
  "id": 2,
  "nome": "Novo usuário",
  "idade": 30,
  "email": "novo_usuario@email.com",
  "cidade": "Rio de Janeiro"
}
```
Se o PUT ou PATCH for bem-sucedido ao atualizar um recurso existente, ele deve retornar o código de status HTTP 200 ou 204, juntamente com a representação atualizada do recurso. Por exemplo:

```json
PUT /users/2

Request Body:
{
  "nome": "Novo usuário update",
  "idade": 35,
  "email": "novo_usuario@email.com",
  "cidade": "Rio de Janeiro"
}

Response:
Status Code: 200 OK
{
  "id": 2,
  "nome": "Novo usuário update",
  "idade": 35,
  "email": "novo_usuario@email.com",
  "cidade": "Rio de Janeiro"
}
```

```json
PATCH /users/2
Request Body:
{
  "name": "Novo usuário patch"
}

Response:
Status Code: 204 No Content
```
Certifique-se de documentar claramente como suas respostas de API serão formatadas e quais códigos de status você usará em diferentes situações. Isso ajudará os clientes da sua API a entender como interagir com ela e a lidar com possíveis erros de forma eficaz.

---

## Filtro, busca e ordenação
Não crie URIs diferentes para buscar recursos com parâmetros de filtro, busca ou ordenação. Tente manter a URI simples e adicione parâmetros de consulta para representar parâmetros ou critérios para buscar um recurso (um único tipo de recurso).

**Filtragem:**

Use parâmetros de consulta definidos na URL para filtrar um recurso do servidor. Por exemplo, se quisermos buscar todos os posts publicados por um usuário, podemos projetar uma API como esta:

```
GET /users/123/posts?state=published
```

No exemplo acima, state é o parâmetro de filtro.

**Busca:**

Para obter resultados com consultas de busca avançadas em vez de filtros básicos, pode-se usar vários parâmetros em uma URI para solicitar a busca de um recurso do servidor.

```
GET /users/123/posts?state=published&ta=scala
```

A consulta acima busca por posts que são publicados com a tag Scala. Hoje em dia, é muito comum usar o Solr como ferramenta de busca, pois ele oferece capacidades avançadas para buscar um documento e você pode projetar sua API da seguinte forma:

```http
GET /users/123/posts?q=sometext&fq=state:published,ta:scala
```

Isso buscará posts para texto livre "sometext" (q) e filtrará os resultados em fq state como publicado e tendo a tag Scala.

**Ordenação:**

Os parâmetros de ordenação ASC e DESC podem ser passados na URL, como por exemplo:

```
GET /users/123/posts?sort=-updated_at
```
Retorna posts ordenados em ordem decrescente de data e hora de atualização.

---

## HATEOAS
HATEOAS (Hypermedia As The Engine Of Application State) é uma restrição da arquitetura REST que permite que um cliente navegue facilmente através de um recurso e suas ações disponíveis sem precisar conhecer como interagir com o aplicativo. Os metadados são incorporados nas respostas do servidor, o que torna a navegação mais fácil.
Para entender melhor, vamos analisar a resposta abaixo de recuperação do usuário com o ID 123 do servidor:

```json
{
   "name": "John Doe",
   "links": [
       {
           "rel": "self",
           "href": "http://localhost:8080/users/123"
       },
       {
           "rel": "posts",
           "href": "http://localhost:8080/users/123/posts"
       },
       {
           "rel": "address",
           "href": "http://localhost:8080/users/123/address"
       }
   ]
}
```
Em alguns casos, pode ser mais simples omitir o formato de links e incluir os links como campos dentro de um recurso, como exemplificado abaixo:
```json
{
   "name": "John Doe",
   "self": "http://localhost:8080/users/123",
   "posts": "http://localhost:8080/users/123",
   "address": "http://localhost:8080/users/123/address"
}
```
A decisão de especificar links como campos de um recurso ou usar o formato de links depende do tamanho e dos campos dos recursos, bem como das ações que podem ser executadas neles. Quando os recursos contêm muitos campos que o usuário pode não querer percorrer, é recomendável fornecer navegação para sub-recursos e implementar o HATEOAS.

---

## Autenticação e autorização sem estado
A autenticação e autorização em APIs REST devem ser stateless. Cada solicitação deve ser auto-suficiente e atendida sem conhecimento da solicitação anterior. Isso é especialmente importante para autorizar ações de usuários.

Anteriormente, os desenvolvedores armazenavam as informações do usuário em sessões no lado do servidor, o que não é uma abordagem escalável. Por isso, cada solicitação deve conter todas as informações de um usuário (se for uma API segura), em vez de depender de solicitações anteriores.

Isso não limita as APIs a um usuário como uma pessoa autorizada, já que também permite a autorização de serviço para serviço. Para autorização de usuário, a combinação de JWT (JSON Web Token) com OAuth2 oferece uma maneira de alcançar isso. Além disso, para comunicação de serviço para serviço, tente passar a chave da API criptografada no cabeçalho.

---
## Swagger para documentação
[Swagger](https://swagger.io/) é uma ferramenta popular para documentação de APIs REST, fornecendo uma maneira de explorar o uso de uma API específica e permitindo que os desenvolvedores entendam o comportamento semântico subjacente. Além disso, existem vários plugins disponíveis para diversas linguagens de programação que permitem gerar o Swagger automaticamente a partir do código-fonte. Esses plugins podem ser facilmente integrados às ferramentas de construção de software existentes, tornando a documentação da API uma parte fácil e automatizada do processo de desenvolvimento. Algumas opções populares incluem o swagger-jaxrs-doclet para Java, o Swashbuckle para .NET, o Flask-RESTPlus para Python e o SpringFox para Spring Framework. Com esses plugins, é possível gerar documentação de API atualizada e precisa com apenas algumas linhas de código.

---

## Códigos de status HTTP

Use códigos de status HTTP para fornecer a resposta a um cliente. Pode ser uma resposta de sucesso ou falha, mas deve definir o que o sucesso ou falha respectivo significa do ponto de vista do servidor.

Abaixo estão as categorias de respostas por seus códigos de status:

**2xx Sucesso**

200 OK: Retornado por uma operação GET ou DELETE bem-sucedida. PUT ou POST também podem usar isso, se o serviço não quiser retornar um recurso de volta ao cliente após a criação ou modificação.

201 Created: Resposta para uma criação de recurso bem-sucedida por uma solicitação POST.

**3xx Redirecionamento**

304 Not Modified: Usado se o cabeçalho de cache HTTP for implementado.

**4xx Erros do cliente**

400 Bad Request: Quando o corpo da solicitação HTTP não pode ser analisado. Por exemplo, se uma API estiver esperando um corpo em formato JSON para uma solicitação POST, mas o corpo da solicitação estiver malformado.

401 Unauthorized: A autenticação não foi bem-sucedida (ou as credenciais não foram fornecidas) ao acessar a API.

403 Forbidden: Se um usuário não está autorizado a realizar uma ação, embora as informações de autenticação estejam corretas.

404 Not Found: Se o recurso solicitado não estiver disponível no servidor.

405 Method Not Allowed: Se o usuário estiver tentando violar um contrato de API, por exemplo, tentando atualizar um recurso usando um método POST.

409 Conflict: O servidor não pôde completar a solicitação devido a um conflito com o estado atual do recurso.

422 Unprocessable Entity: O servidor entende o tipo de conteúdo da solicitação, mas não pode processá-lo devido a erros semânticos.

**5xx Erros do servidor**

Esses erros ocorrem devido a falhas do servidor ou problemas com a infraestrutura subjacente.

_É importante lembrar que esses são alguns dos status codes mais comuns e amplamente utilizados. Existem muitos outros códigos de status HTTP que podem ser usados para fornecer uma resposta mais precisa ao cliente. Para obter uma lista completa e detalhada de todos os códigos de status HTTP, você pode acessar a documentação oficial no seguinte link: https://datatracker.ietf.org/doc/html/rfc7231._

---

## Conclusão

É importante lembrar que seguir as boas práticas de design de APIs RESTful não só torna a API mais fácil de usar, mas também melhora a segurança, escalabilidade e manutenção do serviço. Ao utilizar os padrões e princípios discutidos neste artigo, você poderá criar APIs mais confiáveis, flexíveis e fáceis de evoluir. Além disso, a maturidade das APIs pode ser facilmente documentada usando o [Modelo de Maturidade Richardson](https://restfulapi.net/richardson-maturity-model/).