No desenvolvimento web moderno, as APIs REST (Representational State Transfer) tornaram-se o padrão de fato para a construção de serviços web escaláveis, de fácil manutenção e compreensão. No cerne de qualquer API RESTful reside um conceito fundamental: o recurso. Compreender o que são recursos, como eles são identificados e como interagimos com eles é primordial para projetar e consumir APIs REST de forma eficaz. Este artigo irá aprofundar a natureza dos recursos em APIs REST, explorando suas características, sua relação com coleções e as operações comuns realizadas sobre eles.
A ideia central por trás do REST é que uma API expõe um conjunto de recursos, e os clientes interagem com esses recursos enviando requisições para seus identificadores únicos. Mas o que exatamente constitui um "recurso"? No contexto de uma API REST, um recurso pode ser quase qualquer coisa que você possa nomear. Pode ser uma entidade tangível como um cliente, um produto ou um pedido. Pode também ser um conceito abstrato como um serviço, uma transação ou um cálculo. O ponto chave é que é um item de interesse que pode ser identificado e manipulado.
Pense na própria internet como uma vasta coleção de recursos. Cada página web, imagem, vídeo ou documento que você acessa online é um recurso, cada um com seu próprio endereço único (URL). As APIs REST adotam essa mesma filosofia. Seja um perfil de usuário em uma plataforma de mídia social, um livro específico em uma biblioteca online ou uma previsão do tempo para uma cidade específica, cada um é um recurso que a API disponibiliza.
Quer uma plataforma integrada, Tudo-em-Um para sua Equipe de Desenvolvedores trabalhar junta com máxima produtividade?
Apidog atende a todas as suas demandas e substitui o Postman por um preço muito mais acessível!
Identificando Recursos: O Papel das URIs
Crucialmente, cada recurso em uma API REST deve ter pelo menos um identificador único. Este identificador é tipicamente um Uniform Resource Identifier (URI). A forma mais comum de URI usada em APIs web é um Uniform Resource Locator (URL), que não apenas identifica o recurso, mas também fornece um meio de localizá-lo.
Por exemplo, em uma API para gerenciar um blog, uma postagem de blog específica pode ser identificada por uma URI como /posts/123
. Aqui, /posts
provavelmente representa uma coleção de postagens, e 123
é o identificador único para uma postagem específica dentro dessa coleção. Da mesma forma, um recurso de usuário pode ser identificado por /users/john.doe
.
O design dessas URIs é um aspecto crítico do design de API. URIs bem projetadas são intuitivas, previsíveis e fáceis para os desenvolvedores entenderem e usarem. Elas devem atuar como um sinal claro, indicando a natureza do recurso que está sendo acessado. Uma boa prática dita o uso de substantivos para representar recursos (por exemplo, /products
, /orders
) em vez de verbos (por exemplo, /getProducts
, /createOrder
). Os métodos HTTP (GET, POST, PUT, DELETE) são então usados para especificar a ação a ser realizada no recurso identificado pela URI.
Recurso vs. Representação: Uma Distinção Chave
É importante entender a diferença entre um recurso e sua representação. Um recurso é a entidade conceitual em si – a "coisa" real (o cliente, o produto, a ideia). Uma representação, por outro lado, é um instantâneo do estado desse recurso em um determinado ponto no tempo, tipicamente formatado em um tipo de mídia específico como JSON (JavaScript Object Notation) ou XML (eXtensible Markup Language).
Quando um cliente solicita um recurso de uma API, ele não recebe o recurso em si (que é um conceito abstrato residindo no servidor). Em vez disso, ele recebe uma representação desse recurso. Por exemplo, se você solicitar /users/jane.doe
, a API pode retornar uma representação JSON como:JSON
{
"id": "jane.doe",
"firstName": "Jane",
"lastName": "Doe",
"email": "jane.doe@example.com",
"dateJoined": "2023-01-15T10:00:00Z"
}
Este objeto JSON não é a Jane Doe em si; é uma representação dos dados dela como armazenados pelo sistema. O mesmo recurso poderia potencialmente ter múltiplas representações. Por exemplo, a API pode também ser capaz de fornecer uma representação XML do mesmo usuário se solicitado pelo cliente através de negociação de conteúdo (usando cabeçalhos HTTP como Accept
).
O estado de um recurso pode mudar ao longo do tempo. Se Jane Doe atualizar seu endereço de e-mail, o recurso de usuário subjacente muda. Requisições subsequentes para /users/jane.doe
então retornarão uma nova representação refletindo este estado atualizado. É aqui que entra a parte de "Transferência de Estado" do REST: os clientes interagem com recursos recuperando e manipulando seu estado através dessas representações.
Coleções: Recursos Que Contêm Outros Recursos
Frequentemente, recursos são agrupados em coleções. Uma coleção é ela própria um recurso. Por exemplo, /posts
em nossa API de blog é um recurso de coleção que contém recursos de postagem individuais. Da mesma forma, /users
seria uma coleção de recursos de usuário.
Quando um cliente envia uma requisição GET para uma URI de coleção como /products
, a API tipicamente retorna uma representação que lista os recursos membros dentro dessa coleção, frequentemente com alguma informação de resumo para cada um. Esta lista pode parecer algo como:JSON
[
{
"id": "prod_abc",
"name": "Laptop Pro 15",
"price": 1299.99,
"link": "/products/prod_abc"
},
{
"id": "prod_xyz",
"name": "Wireless Mouse Ergonomic",
"price": 39.99,
"link": "/products/prod_xyz"
},
// ... mais produtos
]
Note como cada item na coleção frequentemente inclui um link (ou sua própria URI) para o recurso individual, permitindo que o cliente navegue e recupere os detalhes completos de um produto específico, se necessário.
O design de URIs para coleções e seus membros segue um padrão lógico. Tipicamente:
/resources
refere-se à coleção (por exemplo,/orders
)./resources/{id}
refere-se a um membro específico dentro dessa coleção (por exemplo,/orders/567
).
Esta estrutura hierárquica torna a API intuitiva e alinha-se com a relação conceitual entre a coleção e seus elementos.
Operações em Recursos e Coleções
A interação com recursos e coleções em uma API REST é alcançada através de métodos HTTP padrão. Esses métodos definem as ações a serem realizadas. Os métodos mais comuns são:
GET: Usado para recuperar uma representação de um recurso ou uma coleção.
GET /posts
recuperaria uma lista de todas as postagens (a coleção).GET /posts/123
recuperaria a postagem específica com ID 123. Requisições GET devem ser seguras (safe), o que significa que não devem ter efeitos colaterais no servidor; são puramente para recuperar dados. Elas também devem ser idempotentes, o que significa que múltiplas requisições GET idênticas devem ter o mesmo efeito que uma única requisição (ou seja, retornar a mesma representação, assumindo que o recurso não mudou nesse meio tempo).
POST: Usado primariamente para criar um novo recurso dentro de uma coleção. O corpo da requisição tipicamente contém a representação do novo recurso a ser criado.
POST /posts
com um corpo de requisição contendo os detalhes de uma nova postagem de blog (por exemplo, título, conteúdo, autor) instruiria o servidor a criar essa nova postagem. O servidor geralmente responde com um status201 Created
e frequentemente inclui a URI do recurso recém-criado no cabeçalhoLocation
da resposta. Requisições POST geralmente não são seguras (pois criam um novo recurso) e não são idempotentes (múltiplas requisições POST idênticas tipicamente criarão múltiplos novos recursos). POST também pode ser usado para outras operações não-idempotentes que não se encaixam perfeitamente em outros métodos HTTP, como acionar um processo ou enviar dados para processamento onde o resultado não é simplesmente uma atualização de um recurso identificável existente.
PUT: Usado para atualizar um recurso existente completamente. O corpo da requisição deve conter a representação completa e nova do recurso. Se o recurso identificado pela URI existir, ele é substituído pela nova representação. Se não existir, algumas APIs podem optar por criá-lo (embora este comportamento possa variar).
PUT /posts/123
com um corpo de requisição contendo o título e conteúdo atualizados para a postagem 123 substituiria a postagem 123 existente pelos novos dados. Requisições PUT não são seguras (pois modificam um recurso) mas são idempotentes. Enviar a mesma requisição PUT múltiplas vezes deve resultar no mesmo estado para o recurso. Por exemplo, atualizar o título de uma postagem para "Novo Título" múltiplas vezes ainda resulta no título sendo "Novo Título".
DELETE: Usado para remover um recurso.
DELETE /posts/123
deletaria a postagem de blog com ID 123. Requisições DELETE não são seguras (removem dados) mas são idempotentes. Deletar um recurso múltiplas vezes deve ter o mesmo resultado que deletá-lo uma vez (o recurso desaparece). Requisições DELETE subsequentes para a mesma URI podem retornar um404 Not Found
ou um204 No Content
.
PATCH: Usado para atualizar parcialmente um recurso existente. Ao contrário do PUT, que exige que o cliente envie a representação inteira do recurso, o PATCH permite enviar apenas as alterações. Por exemplo, para atualizar apenas o endereço de e-mail de um usuário, uma requisição PATCH precisaria incluir apenas o novo e-mail.
PATCH /users/jane.doe
com um corpo de requisição como{"email": "new.email@example.com"}
atualizaria apenas o endereço de e-mail de Jane, deixando outros campos como o nome inalterados. Requisições PATCH não são seguras. Sua idempotência pode ser debatida e depende da natureza da operação de patch. Por exemplo, uma operação PATCH que diz "anexar '!' à descrição" não é idempotente, enquanto "definir a descrição para 'novo valor'" é.
Recursos Singleton
Embora coleções e seus membros sejam comuns, às vezes um recurso é uma entidade autônoma, frequentemente referida como um recurso "singleton". Um bom exemplo pode ser a configuração de uma aplicação específica ou o status atual de um sistema.
Por exemplo, /application/configuration
poderia ser uma URI para um recurso singleton representando as configurações de configuração da aplicação. Uma requisição GET
para esta URI recuperaria a configuração atual, e uma requisição PUT
poderia ser usada para atualizá-la. Não há uma "coleção" de configurações neste contexto; há apenas a configuração.
Da mesma forma, /system/status
poderia representar o status operacional atual do sistema.
Melhores Práticas para Projetar APIs Baseadas em Recursos
Projetar uma API centrada em recursos envolve mais do que apenas identificar entidades e mapeá-las para URIs. Várias melhores práticas contribuem para uma API robusta e amigável ao usuário:
- Usar Substantivos para URIs: Como mencionado anteriormente, as URIs de recursos devem ser substantivos (por exemplo,
/products
,/users/{userId}/orders
). Verbos devem ser reservados para métodos HTTP. - Nomenclatura de URI Consistente: Use uma convenção de nomenclatura consistente para suas URIs. Substantivos plurais são geralmente preferidos para coleções (por exemplo,
/customers
em vez de/customer
). Use hifens (-
) para melhorar a legibilidade de segmentos de caminho longos (por exemplo,/product-categories
) em vez de underscores (_
) ou camelCase. - Manter URIs Simples e Hierárquicas: Projete URIs que reflitam as relações entre recursos. Por exemplo,
/users/{userId}/accounts/{accountId}
mostra claramente que uma conta pertence a um usuário. No entanto, evite aninhamento excessivamente profundo, o que pode tornar as URIs difíceis de usar. - Statelessness (Sem Estado): Cada requisição de um cliente para o servidor deve conter todas as informações necessárias para entender1 e processar a requisição. O servidor2 não deve armazenar nenhum contexto do cliente entre requisições. Este é um princípio central do REST e contribui para a escalabilidade.
- Alavancar Métodos HTTP Corretamente: Use GET, POST, PUT, DELETE e PATCH de acordo com suas semânticas definidas. Não use GET para modificar dados ou POST para recuperar dados quando GET for apropriado.
- Usar Códigos de Status HTTP Apropriadamente: Retorne códigos de status HTTP padrão para indicar o resultado de uma requisição (por exemplo,
200 OK
,201 Created
,204 No Content
,400 Bad Request
,401 Unauthorized
,403 Forbidden
,3404 Not Found
,500 Internal Server Error
). Isso fornece feedback claro ao cliente. - Suportar Negociação de Conteúdo: Permita que os clientes especifiquem o formato de representação desejado (por exemplo, JSON, XML) usando o cabeçalho
Accept
e indique o formato do corpo da requisição usando o cabeçalhoContent-Type
. - Versionamento: Planeje a evolução da API implementando uma estratégia de versionamento (por exemplo,
/v1/products
). Isso permite introduzir mudanças que quebram a compatibilidade sem afetar clientes existentes. - Fornecer Representações de Erro Significativas: Quando um erro ocorrer, retorne uma mensagem de erro útil no corpo da resposta (tipicamente JSON ou XML) que explique o que deu errado.
- Hypermedia as the Engine of Application State (HATEOAS): Embora nem sempre totalmente implementado, HATEOAS é um princípio chave do REST. Significa que as representações de recursos devem incluir links (controles de hipermídia) que permitam aos clientes descobrir ações e recursos relacionados. Por exemplo, uma representação de um pedido pode incluir links para cancelar o pedido, ver seu status de envio ou ver os produtos que ele contém. Isso torna a API mais autodescritiva.
A Granularidade dos Recursos
Um desafio comum de design é determinar a granularidade apropriada de seus recursos. Um endereço deve ser um recurso separado, ou parte de um recurso de usuário? Itens de pedido devem ser recursos distintos, ou incorporados dentro de um recurso de pedido?
Não há uma resposta única e certa; depende dos casos de uso específicos e de como os clientes tipicamente interagirão com os dados.
- Recursos Separados: Se uma entidade (como um endereço) pode ser criada, recuperada, atualizada ou deletada independentemente, ou se é compartilhada entre múltiplos outros recursos, frequentemente faz sentido modelá-la como um recurso separado com sua própria URI (por exemplo,
/addresses/{addressId}
). Você pode então linká-la a partir de outros recursos (por exemplo, um recurso de usuário pode ter um campoaddressId
ou um link para/addresses/{addressId}
). - Recursos Incorporados/Sub-recursos: Se uma entidade está rigidamente acoplada a um recurso pai e não tem um ciclo de vida independente, pode ser melhor modelada como parte da representação do recurso pai ou como um sub-recurso acessível via um caminho como
/users/{userId}/address
. Isso pode simplificar as interações do cliente se a sub-entidade for sempre acessada no contexto de seu pai.
A escolha frequentemente envolve trade-offs:
- Chattiness (Excesso de Comunicação): Muitos recursos finamente granulares podem levar a mais requisições HTTP (aumento da chattiness) se um cliente precisar montar um quadro completo a partir de múltiplas fontes.
- Duplicação/Complexidade de Dados: Incorporar recursos pode levar a payloads maiores e potencial duplicação de dados ou complexidade se a informação incorporada também estiver disponível como um recurso autônomo.
- Cacheability (Capacidade de Cache): Recursos separados são frequentemente mais fáceis de armazenar em cache independentemente.
- Atomicidade de Operações: Atualizações para um único recurso grosseiramente granular são inerentemente atômicas. Gerenciar a atomicidade em múltiplas atualizações de recursos finamente granulares pode ser mais complexo.
A consideração cuidadosa desses fatores, juntamente com uma compreensão profunda de como a API será usada, é crucial para tomar as decisões corretas sobre a granularidade dos recursos.
Quer uma plataforma integrada, Tudo-em-Um para sua Equipe de Desenvolvedores trabalhar junta com máxima produtividade?
Apidog atende a todas as suas demandas e substitui o Postman por um preço muito mais acessível!
Conclusão
Recursos são os blocos de construção fundamentais de qualquer API RESTful. Eles representam as "coisas" que uma API expõe e permite que os clientes interajam. Ao atribuir URIs únicas aos recursos, diferenciando entre um recurso e sua representação, e organizando recursos em coleções lógicas, os desenvolvedores podem criar APIs que são intuitivas, escaláveis e aderem aos princípios do REST.
Compreender como definir, identificar e manipular recursos usando métodos HTTP padrão é essencial tanto para designers de API quanto para consumidores. Juntamente com as melhores práticas em design de URI, uso apropriado de códigos de status HTTP e uma abordagem cuidadosa à granularidade dos recursos, um modelo de recurso bem definido leva a APIs que não são apenas funcionais, mas também agradáveis de trabalhar. À medida que o cenário digital continua a evoluir, os princípios da arquitetura orientada a recursos permanecerão um pilar da comunicação eficaz de serviços web.