REST API: Porquê você complica?

Fazendo uma API REST detalhada

6 de dez. de 2025, 02:12

Esta apresentação NÃO é sobre:

Porém se possui interesse nessses temas, recomendo muito o curso FastAPI do Zero publicado pelo Eduardo Mendes.

Esta apresentação é sobre

A web , REST API e Hipermidia

Se você atua na área de tecnologia como desenvolvedor de sofware para a web, já deve estar familiarizado com o termo API(Application Progaming Interface - Interface de Programação de Aplicação), que pode ser toda e quaisquer aplicação que tem como objetivo interfacear de forrma programavél algum recurso, indo de consulta a base de dados a acesso a recursos de hardware como GPU.

Exemplos de API que podemos citar seria o uso de GPU por meio do navegador, através da API WebGL

Porém pensando em aplicações web, uma API REST(ful) - Representational State Transfer, refere-se a uma forma de controlar e restringir uma aplicação por meio de representação de estados destas em recursos. Atualmente o formato mais comun de implementação consiste em um servidor Web que por meio do protocolo HTTP (Hipertext Transfer Protocol) que entrega as informações a respeito dos estados em estrutura JSON (Javascript Object Notation).

Uma aplicação REST que contemple todos os princípios referentes á ideopotência, que é a capacidade de representar todos os esttados necessários de forma semântica, usando os verbos adequados (GET, POST, PUT, PATCH, DELETE...) bem como respeitando os principíos de se manter sem estados entre requisições, ser cacheavél e possuir mensagens expresivas em caso de falha é denominada de RESTful, porém podemos usar o termo em questão de forma análoga ao próprio conceito de REST API em linhas gerais.

Exemplo prático e resumido

Uma API REST é como um restaurante, onde o estabelecimento é o servidor, o cliente por sua vez está implicito no nome.

Um exeplo de API REST aberta ao público seria a API OMDb, Open Movie Databse

Cabeçarios HTTP (Headers)

Cabeçarios são informações extras que podem ser enviadas , bem como recebidas por uma ou para uma API REST, nestes em formatos de texto (string) detalhamos informações e metaddos no geral sobre o conteúdo trafegado, coisas como informar a origem da aplicação, realizar o envio de chaves de autentcação e até mesmo informar a aplicação cliente a política de cache do recurso.

Exemplos de cabeçários comuns

image

HATEOS (Hypermidia as a the Engine of the Application State)

Nesta conversa, o termo Hipermidia, mais precisamente o acrônomo Hypermedia as the engine of application state (HATEOAS), seria uma forma de implementação de arquitetura para REST API que por meio das hipermidias, torna a consulta e navegação pelos recursos mais dinâmica, conseguindo comunicar por meio de metadados e padrões, formas de navegação e integração entre os recursos disponibilizados.

A principal e mais comun forma de implementação de HATEOS em aplicações modernas é dada por meio da implementação de recursos com HAL - Hypertext Application Language, onde adicionamos a resposta do dado, possivéis links relacionados a este, como exemplo a resposta abaixo.

{
  "_links": {
    "self": {
      "href": "http://example.com/api/book/hal-cookbook"
    }
  },
  "id": "hal-cookbook",
  "name": "HAL Cookbook"
}

Além do HAL, outros formatos para implementar HATEOS seriam JSON:API, Siren, Hydra (JSON-LD), Collection+JSON e UBER.


JSON:API

{
  "data": {
    "type": "book",
    "id": "1",
    "attributes": {
      "title": "O Senhor dos Anéis"
    },
    "links": {
      "self": "/books/1"
    },
    "relationships": {
      "author": {
        "links": {
          "related": "/books/1/author"
        }
      }
    }
  },
  "links": {
    "self": "/books"
  }
}

Siren

{
  "class": ["book"],
  "properties": {
    "id": 1,
    "title": "O Senhor dos Anéis"
  },
  "links": [
    { "rel": ["self"], "href": "/books/1" },
    { "rel": ["author"], "href": "/books/1/author" }
  ],
  "actions": [
    {
      "name": "update-book",
      "method": "PUT",
      "href": "/books/1",
      "type": "application/json",
      "fields": [
        { "name": "title", "type": "text" }
      ]
    }
  ]
}

Hydra (JSON-LD)

{
  "@context": "/contexts/Book.jsonld",
  "@id": "/books/1",
  "@type": "Book",
  "title": "O Senhor dos Anéis",
  "author": "/books/1/author",
  "hydra:operation": [
    {
      "@type": "hydra:UpdateResourceOperation",
      "hydra:method": "PUT",
      "hydra:expects": "http://schema.org/Book",
      "hydra:returns": "http://schema.org/Book",
      "hydra:target": "/books/1"
    }
  ]
}

Collection+JSON

{
  "collection": {
    "version": "1.0",
    "href": "/books",
    "items": [
      {
        "href": "/books/1",
        "data": [
          { "name": "id", "value": "1" },
          { "name": "title", "value": "O Senhor dos Anéis" }
        ],
        "links": [
          { "rel": "author", "href": "/books/1/author" }
        ]
      }
    ],
    "links": [
      { "rel": "self", "href": "/books" }
    ],
    "queries": [
      {
        "rel": "search",
        "href": "/books/search",
        "data": [
          { "name": "title", "value": "" }
        ]
      }
    ]
  }
}

UBER (Uber Hypermedia)

{
  "uber": {
    "version": "1.0",
    "data": [
      {
        "id": "book",
        "name": "O Senhor dos Anéis",
        "rel": ["self"],
        "url": "/books/1",
        "data": [
          {
            "rel": ["author"],
            "url": "/books/1/author"
          }
        ]
      }
    ]
  }
}

HATEOS é complicado, mas não deveria ser

A principal dificuldade dos formatos comuns para HATEOS gira em torno da dificuldade de serialização:

A serialização mal otimizada pode dificultar a performance de aplicações dinâmicas, tais como feeds de redes sociais, leitura de dados em tempo real e procesos de digestão de alto volume de dados.

Outro problema menos evidente seria a obrigatoriedade de manter implementações sequênciais de problemas que seriam resolvidos de uma forma melhor por meio concorrência/paralelismo

Como resolver o problema

A solução para as dificuldades geradas pela implementação de uma arquitetura HATEOS consiste em atender os seguintes pré-requisitos

A forma mais simples de resolver isso seria por meio da implementação da RFC 8288 - Web Linking para a paginação e demais informações por meio de cabeçários customizados.

Ainda que muitos serviços usem o prefixo X- para headers especiais, atualmente isso é desencorajado especificamente pela RFC 6648

Desta forma, podemos recompoer nosso exemplo de livros por meio do seguinte modelo para o corpo da resposta (Response Body) em uma simples lista de JSON.

[
  {
    "id": 1,
    "title": "O Senhor dos Anéis",
    "author": "R. R. Tolkein"
  }
]

Por fim, podemos ter os seguintes cabeçãrios, nem todos são obrigatórios por questões de performance, principalmente aqueles que realizam a contagem de items, porém o Items-Returned é uma ótima forma de saber se a lista retornou sem item algum (vazia).

HTTP/1.1 200 OK
Content-Type: application/json
Total-Count: 1000               # Total de itens no banco de dados
Total-Pages: 100                # Total de páginas (se você já calculou)
Items-Per-Page: 10              # Itens por página
Current-Page: 1                 # Página atual
Items-Returned: 1              # Total de itens retornados na página atual
Link: <https://api.exemplo.com/items?page=2>; rel="next",
      <https://api.exemplo.com/items?page=100>; rel="last",
      <https://api.exemplo.com/items?page=1>; rel="first"
      <https://api.exemplo.com/items?page=1>; rel="prev"

Considerando cenários de requisições de criação de dados , principalmente de objetos únicos, podemos usar o header Location caso este possua algum endpoint de consulta por meio de id de referência que seja único e ideopotente.

Outro ponto importante é que nem sempre poderemos configurar o domínio, nossa API pode estar limitada por um API Getway ou ferramenta de DNS, nesses casos é recomendado o uso de links relativos, isso cria um ônus para o cliente concatenar ao dôminio original, mas este é disponiblizado no header Host.

Dessa forma o exemplo que utilizamos anteriormente é capaz de evoluir para esse modelo

HTTP/1.1 200 OK
Content-Type: application/json
Total-Count: 1000               # Total de itens no banco de dados
Total-Pages: 100                # Total de páginas (se você já calculou)
Items-Per-Page: 10              # Itens por página
Current-Page: 1                 # Página atual
Items-Returned: 1              # Total de itens retornados na página atual
Link: </items?page=2>; rel="next",
      </items?page=100>; rel="last",
      </items?page=1>; rel="first"
      </items?page=1>; rel="prev"

O uso de contadores deve ser moderado, visto que em bases grandes de dados , ou com mudanças constantes, essa operação pode custar muito recurso, nesses casos cabe ao desenvolvedor responsavél, avaliar as necessidades e capacidades do projeto.

Status Code

StatusCode são números de 3 dígitos entregues numa requisição http como forma de comunicar o retorno para uma requisição de forma imediata, sem precisar de serialização ou informações adicionais para tomar decisões básicas.

É importante que estes sejam usados de forma consistente, ao passo que clientes automatizados como frameworks possam consumir sua API sem muito workarround

Podemos dividir os status code em 5 faixas.

  1. 1xx: status de informação, usados para comunicar algum tipo de informação a respeito do servidor.
    • 100: continue, muito usado quando se quer evitar o envio de um payload muito grande mas precisa verificar se o servidor está disponivél
      • Um caso de uso curioso que eu vi isso em prática foi em fila de geração de PDF de apólices em sistemas de seguradora.
  2. 2xx: status de sucesso (é o que esperamos que aconteça no caminho feliz), representa uma operação bem sucedida.
    • 200: Ok, simplesmente Ok
    • 201: Content created, um puxão de orelha nos devs que deveriam usar mais esse status, principalmente quando uma operação resulta em algum tipo de dado criado, como vimos, métodos POST e PUT.
    • 202: Accepted, quando uma requisição é aceita , porém o resultado será mandado depois, isso é crucial quando se faz http polling, que é o processo de ficar verificando no servidor até a informação está pronta , quando retorna um status 200, muito comun em sistemas de fila de impressão mais antigos ou de fila de arquivos gerados em background.
    • 204: No content, a operação deu certo, mas não tem body algum a ser retornado, porém os headers são úteis e se necessário, deve ser atualizado cache, em casos de DELETE é o ideal a ser usado na maioria dos cenários*
  3. 3xx: status de redirecionamento, servem para informar os possivéis motivos de dados terem sido migrados
    • 301: Moved Permanently, siginifica que o conteúdo foi movido de url, a nova será disponibilizada no response.
    • 302: Found, siginifica que o conteúdo foi achado e movido temporariamente, pode representar uma nova url e um novo método.
    • 307: Temporary Redirect, semântica igual ao 302, porém significa que o método HTTP não deve ser mudado no redirect, apenas a url.
  4. 4xx: status de erros causados pelo cliente culpa do frontend, quando algum dado enviado ou requisição feita pelo lado cliente é inconsistente.
    • 404: Não encontrado, para rota ou conteúdo não encontrado, origem do meme 404: Not Found
    • 401: Não autenticado, siginifica que o usuário não forneceu a informação que determina que ele foi autenticado em algum momento, usado como identificador de endpoints protegidos, ou seja , endpoints disponivéis apenas para usuários autenticados.
    • 403: Forbidden, siginifica que o usuário, independente de autenticado ou não, não possui acesso a informação devida.
    • 418: I'm a teapot (Eu sou uma chaleira), servidor se recusa a fazer café.
  5. 5xx: status de erro causados pelo servidor culpa sua, usado principalmente para erros de mecanismos internos/
    • 500: Internal Server Error, quando seu servidor tem um erro de execução , runtime ou um erro não tratavél, em ambiente de desenvolvimento e nos logs, normalmente consta a stacktrace que ocasionou a falha.
    • 502: Bad Getway: Quando um servidor de Getway falha em achar ou realizar a conexão ocasionada pelo servidor de origem, normalmente associado a timeout entre o getway e o servidor consumido por este.
    • 503: Service Unavailable, mostado quando há uma falha temporária, em conjunto com uma página de erro amigavél se possivél para explicar de forma breve a falha, em cassos de serviços como aplicação, é recomendado devolver o Header Retry-After com informações de qunado está previsto a voltar o recurso. Era muito comun no período de transição para a Web 2.0 (Twiter lá por 2010 e o famigerado "Rails não escala")

Dicas e macetes para uso coerente de status code

Referências