Dans le développement web moderne, les API Representational State Transfer (REST) sont devenues la norme de facto pour la création de services web évolutifs, maintenables et faciles à comprendre. Au cœur de toute API RESTful se trouve un concept fondamental : la ressource. Comprendre ce que sont les ressources, comment elles sont identifiées et comment nous interagissons avec elles est primordial pour concevoir et consommer efficacement les API REST. Cet article approfondira la nature des ressources dans les API REST, en explorant leurs caractéristiques, leur relation avec les collections et les opérations courantes effectuées sur elles.
L'idée de base derrière REST est qu'une API expose un ensemble de ressources, et les clients interagissent avec ces ressources en envoyant des requêtes à leurs identifiants uniques. Mais qu'est-ce qui constitue exactement une "ressource" ? Dans le contexte d'une API REST, une ressource peut être presque tout ce que vous pouvez nommer. Il pourrait s'agir d'une entité tangible comme un client, un produit ou une commande. Il pourrait également s'agir d'un concept abstrait tel qu'un service, une transaction ou un calcul. L'essentiel est qu'il s'agisse d'un élément d'intérêt qui peut être identifié et manipulé.
Considérez Internet lui-même comme une vaste collection de ressources. Chaque page web, image, vidéo ou document auquel vous accédez en ligne est une ressource, chacune avec sa propre adresse unique (URL). Les API REST adoptent cette même philosophie. Qu'il s'agisse d'un profil d'utilisateur sur une plateforme de médias sociaux, d'un livre spécifique dans une bibliothèque en ligne ou des prévisions météorologiques pour une ville particulière, chacun est une ressource que l'API met à disposition.
Vous voulez une plateforme intégrée, tout-en-un, pour que votre équipe de développeurs travaille ensemble avec une productivité maximale ?
Apidog répond à toutes vos demandes et remplace Postman à un prix beaucoup plus abordable !
Identifier les ressources : le rôle des URI
Fondamentalement, chaque ressource d'une API REST doit avoir au moins un identifiant unique. Cet identifiant est généralement un Uniform Resource Identifier (URI). La forme la plus courante d'URI utilisée dans les API web est un Uniform Resource Locator (URL), qui non seulement identifie la ressource, mais fournit également un moyen de la localiser.
Par exemple, dans une API pour la gestion d'un blog, un article de blog spécifique pourrait être identifié par un URI comme /posts/123
. Ici, /posts
représente probablement une collection d'articles, et 123
est l'identifiant unique d'un article particulier au sein de cette collection. De même, une ressource utilisateur pourrait être identifiée par /users/john.doe
.
La conception de ces URI est un aspect essentiel de la conception d'API. Les URI bien conçus sont intuitifs, prévisibles et faciles à comprendre et à utiliser pour les développeurs. Ils doivent agir comme un panneau indicateur clair, indiquant la nature de la ressource à laquelle on accède. Les bonnes pratiques dictent l'utilisation de noms pour représenter les ressources (par exemple, /products
, /orders
) plutôt que des verbes (par exemple, /getProducts
, /createOrder
). Les méthodes HTTP (GET, POST, PUT, DELETE) sont ensuite utilisées pour spécifier l'action à effectuer sur la ressource identifiée par l'URI.
Ressource vs. Représentation : une distinction clé
Il est important de comprendre la différence entre une ressource et sa représentation. Une ressource est l'entité conceptuelle elle-même - la "chose" réelle (le client, le produit, l'idée). Une représentation, en revanche, est un instantané de l'état de cette ressource à un moment donné, généralement formaté dans un type de média spécifique comme JSON (JavaScript Object Notation) ou XML (eXetensible Markup Language).
Lorsqu'un client demande une ressource à une API, il ne reçoit pas la ressource elle-même (qui est un concept abstrait résidant sur le serveur). Au lieu de cela, il reçoit une représentation de cette ressource. Par exemple, si vous demandez /users/jane.doe
, l'API pourrait renvoyer une représentation JSON comme :JSON
{
"id": "jane.doe",
"firstName": "Jane",
"lastName": "Doe",
"email": "jane.doe@example.com",
"dateJoined": "2023-01-15T10:00:00Z"
}
Cet objet JSON n'est pas Jane Doe elle-même ; c'est une représentation de ses données telles qu'elles sont stockées par le système. La même ressource pourrait potentiellement avoir plusieurs représentations. Par exemple, l'API pourrait également être capable de fournir une représentation XML du même utilisateur si le client le demande par le biais de la négociation de contenu (en utilisant des en-têtes HTTP comme Accept
).
L'état d'une ressource peut changer au fil du temps. Si Jane Doe met à jour son adresse e-mail, la ressource utilisateur sous-jacente change. Les requêtes ultérieures pour /users/jane.doe
renverront alors une nouvelle représentation reflétant cet état mis à jour. C'est là que la partie "Transfert d'état" de REST entre en jeu : les clients interagissent avec les ressources en récupérant et en manipulant leur état par le biais de ces représentations.
Collections : Ressources qui contiennent d'autres ressources
Souvent, les ressources sont regroupées en collections. Une collection est elle-même une ressource. Par exemple, /posts
dans notre API de blog est une ressource de collection qui contient des ressources d'articles individuels. De même, /users
serait une collection de ressources utilisateur.
Lorsqu'un client envoie une requête GET à un URI de collection comme /products
, l'API renvoie généralement une représentation qui liste les ressources membres de cette collection, souvent avec des informations sommaires pour chacune d'elles. Cette liste pourrait ressembler à ceci :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"
},
// ... more products
]
Notez comment chaque élément de la collection inclut souvent un lien (ou son propre URI) vers la ressource individuelle, permettant au client de naviguer vers et de récupérer les détails complets d'un produit spécifique si nécessaire.
La conception des URI pour les collections et leurs membres suit un schéma logique. Généralement :
/resources
fait référence à la collection (par exemple,/orders
)./resources/{id}
fait référence à un membre spécifique au sein de cette collection (par exemple,/orders/567
).
Cette structure hiérarchique rend l'API intuitive et s'aligne sur la relation conceptuelle entre la collection et ses éléments.
Opérations sur les ressources et les collections
L'interaction avec les ressources et les collections dans une API REST est réalisée via des méthodes HTTP standard. Ces méthodes définissent les actions à effectuer. Les méthodes les plus courantes sont :
GET : Utilisé pour récupérer une représentation d'une ressource ou d'une collection.
GET /posts
récupérerait une liste de tous les articles (la collection).GET /posts/123
récupérerait l'article spécifique avec l'ID 123. Les requêtes GET doivent être sûres, ce qui signifie qu'elles ne doivent avoir aucun effet secondaire sur le serveur ; elles sont purement destinées à récupérer des données. Elles doivent également être idempotentes, ce qui signifie que plusieurs requêtes GET identiques doivent avoir le même effet qu'une seule requête (c'est-à-dire renvoyer la même représentation, en supposant que la ressource n'a pas changé entre-temps).
POST : Principalement utilisé pour créer une nouvelle ressource au sein d'une collection. Le corps de la requête contient généralement la représentation de la nouvelle ressource à créer.
POST /posts
avec un corps de requête contenant les détails d'un nouvel article de blog (par exemple, titre, contenu, auteur) ordonnerait au serveur de créer ce nouvel article. Le serveur répond généralement avec un statut201 Created
et inclut souvent l'URI de la ressource nouvellement créée dans l'en-têteLocation
de la réponse. Les requêtes POST ne sont généralement pas sûres (car elles créent une nouvelle ressource) et ne sont pas idempotentes (plusieurs requêtes POST identiques créeront généralement plusieurs nouvelles ressources). POST peut également être utilisé pour d'autres opérations non idempotentes qui ne correspondent pas parfaitement aux autres méthodes HTTP, telles que le déclenchement d'un processus ou l'envoi de données pour traitement lorsque le résultat n'est pas simplement une mise à jour d'une ressource identifiable existante.
PUT : Utilisé pour mettre à jour une ressource existante complètement. Le corps de la requête doit contenir la nouvelle représentation complète de la ressource. Si la ressource identifiée par l'URI existe, elle est remplacée par la nouvelle représentation. Si elle n'existe pas, certaines API peuvent choisir de la créer (bien que ce comportement puisse varier).
PUT /posts/123
avec un corps de requête contenant le titre et le contenu mis à jour pour l'article 123 remplacerait l'article 123 existant par les nouvelles données. Les requêtes PUT ne sont pas sûres (car elles modifient une ressource) mais sont idempotentes. L'envoi de la même requête PUT plusieurs fois doit entraîner le même état pour la ressource. Par exemple, la mise à jour du titre d'un article à "Nouveau titre" plusieurs fois entraîne toujours le titre "Nouveau titre".
DELETE : Utilisé pour supprimer une ressource.
DELETE /posts/123
supprimerait l'article de blog avec l'ID 123. Les requêtes DELETE ne sont pas sûres (elles suppriment des données) mais sont idempotentes. La suppression d'une ressource plusieurs fois doit avoir le même résultat que sa suppression une seule fois (la ressource a disparu). Les requêtes DELETE ultérieures vers le même URI peuvent renvoyer un404 Not Found
ou un204 No Content
.
PATCH : Utilisé pour mettre à jour partiellement une ressource existante. Contrairement à PUT, qui exige que le client envoie l'intégralité de la représentation de la ressource, PATCH permet d'envoyer uniquement les modifications. Par exemple, pour mettre à jour uniquement l'adresse e-mail d'un utilisateur, une requête PATCH n'aurait besoin d'inclure que le nouvel e-mail.
PATCH /users/jane.doe
avec un corps de requête comme{"email": "new.email@example.com"}
mettrait à jour uniquement l'adresse e-mail de Jane, laissant les autres champs comme son nom inchangés. Les requêtes PATCH ne sont pas sûres. Leur idempotence peut être débattue et dépend de la nature de l'opération de correctif. Par exemple, une opération PATCH qui dit "ajouter '!' à la description" n'est pas idempotente, alors que "définir la description sur 'nouvelle valeur'" l'est.
Ressources Singleton
Bien que les collections et leurs membres soient courants, une ressource est parfois une entité autonome, souvent appelée ressource "singleton". Un bon exemple pourrait être la configuration d'une application spécifique ou l'état actuel d'un système.
Par exemple, /application/configuration
pourrait être un URI pour une ressource singleton représentant les paramètres de configuration de l'application. Une requête GET
vers cet URI récupérerait la configuration actuelle, et une requête PUT
pourrait être utilisée pour la mettre à jour. Il n'y a pas de "collection" de configurations dans ce contexte ; il y a juste la configuration.
De même, /system/status
pourrait représenter l'état opérationnel actuel du système.
Meilleures pratiques pour la conception d'API basées sur les ressources
La conception d'une API centrée sur les ressources implique plus que simplement l'identification des entités et leur mappage vers des URI. Plusieurs bonnes pratiques contribuent à une API robuste et conviviale :
- Utiliser des noms pour les URI : Comme mentionné précédemment, les URI de ressources doivent être des noms (par exemple,
/products
,/users/{userId}/orders
). Les verbes doivent être réservés aux méthodes HTTP. - Nommage cohérent des URI : Utilisez une convention de nommage cohérente pour vos URI. Les noms pluriels sont généralement préférés pour les collections (par exemple,
/customers
au lieu de/customer
). Utilisez des traits d'union (-
) pour améliorer la lisibilité des longs segments de chemin (par exemple,/product-categories
) plutôt que des traits de soulignement (_
) ou camelCase. - Garder les URI simples et hiérarchiques : Concevez des URI qui reflètent les relations entre les ressources. Par exemple,
/users/{userId}/accounts/{accountId}
montre clairement qu'un compte appartient à un utilisateur. Cependant, évitez les imbrications trop profondes, ce qui peut rendre les URI difficiles à manier. - Sans état : Chaque requête d'un client vers le serveur doit contenir toutes les informations nécessaires pour comprendre1 et traiter la requête. Le serveur2 ne doit pas stocker de contexte client entre les requêtes. Il s'agit d'un principe fondamental de REST et contribue à l'évolutivité.
- Tirer parti correctement des méthodes HTTP : Utilisez GET, POST, PUT, DELETE et PATCH en fonction de leur sémantique définie. N'utilisez pas GET pour modifier des données ou POST pour récupérer des données lorsque GET est approprié.
- Utiliser les codes d'état HTTP de manière appropriée : Renvoyez les codes d'état HTTP standard pour indiquer le résultat d'une requête (par exemple,
200 OK
,201 Created
,204 No Content
,400 Bad Request
,401 Unauthorized
,403 Forbidden
,3404 Not Found
,500 Internal Server Error
). Cela fournit des commentaires clairs au client. - Prendre en charge la négociation de contenu : Permettez aux clients de spécifier le format de représentation souhaité (par exemple, JSON, XML) à l'aide de l'en-tête
Accept
et d'indiquer le format du corps de la requête à l'aide de l'en-têteContent-Type
. - Versionnement : Prévoyez l'évolution de l'API en mettant en œuvre une stratégie de versionnement (par exemple,
/v1/products
). Cela vous permet d'introduire des changements importants sans affecter les clients existants. - Fournir des représentations d'erreur significatives : Lorsqu'une erreur se produit, renvoyez un message d'erreur utile dans le corps de la réponse (généralement JSON ou XML) qui explique ce qui s'est mal passé.
- Hypermedia en tant que moteur de l'état de l'application (HATEOAS) : Bien que pas toujours entièrement mis en œuvre, HATEOAS est un principe clé de REST. Cela signifie que les représentations des ressources doivent inclure des liens (contrôles hypermédia) qui permettent aux clients de découvrir les actions et les ressources associées. Par exemple, une représentation d'une commande pourrait inclure des liens pour annuler la commande, afficher son état d'expédition ou voir les produits qu'elle contient. Cela rend l'API plus autodécouvrable.
La granularité des ressources
Un défi de conception courant consiste à déterminer la granularité appropriée de vos ressources. Une adresse doit-elle être une ressource distincte ou faire partie d'une ressource utilisateur ? Les éléments de commande doivent-ils être des ressources distinctes ou intégrés dans une ressource de commande ?
Il n'y a pas de réponse unique ; cela dépend des cas d'utilisation spécifiques et de la manière dont les clients interagiront généralement avec les données.
- Ressources distinctes : Si une entité (comme une adresse) peut être créée, récupérée, mise à jour ou supprimée indépendamment, ou si elle est partagée entre plusieurs autres ressources, il est souvent judicieux de la modéliser comme une ressource distincte avec son propre URI (par exemple,
/addresses/{addressId}
). Vous pouvez ensuite la lier à partir d'autres ressources (par exemple, une ressource utilisateur peut avoir un champaddressId
ou un lien vers/addresses/{addressId}
). - Ressources intégrées/Sous-ressources : Si une entité est étroitement liée à une ressource parente et n'a pas de cycle de vie indépendant, il pourrait être préférable de la modéliser dans le cadre de la représentation de la ressource parente ou en tant que sous-ressource accessible via un chemin tel que
/users/{userId}/address
. Cela peut simplifier les interactions client si la sous-entité est toujours accessible dans le contexte de son parent.
Le choix implique souvent des compromis :
- Bavardage : De nombreuses ressources à grain fin pourraient entraîner davantage de requêtes HTTP (bavardage accru) si un client doit assembler une image complète à partir de plusieurs sources.
- Duplication/Complexité des données : L'intégration de ressources peut entraîner des charges utiles plus volumineuses et une duplication ou une complexité potentielles des données si les informations intégrées sont également disponibles en tant que ressource autonome.
- Mise en cache : Les ressources distinctes sont souvent plus faciles à mettre en cache indépendamment.
- Atomicité des opérations : Les mises à jour d'une seule ressource à grain grossier sont intrinsèquement atomiques. La gestion de l'atomicité sur plusieurs mises à jour de ressources à grain fin peut être plus complexe.
Une considération attentive de ces facteurs, ainsi qu'une compréhension approfondie de la manière dont l'API sera utilisée, est cruciale pour prendre les bonnes décisions concernant la granularité des ressources.
Vous voulez une plateforme intégrée, tout-en-un, pour que votre équipe de développeurs travaille ensemble avec une productivité maximale ?
Apidog répond à toutes vos demandes et remplace Postman à un prix beaucoup plus abordable !
Conclusion
Les ressources sont les blocs de construction fondamentaux de toute API RESTful. Elles représentent les "choses" qu'une API expose et permet aux clients d'interagir avec. En attribuant des URI uniques aux ressources, en différenciant une ressource de sa représentation et en organisant les ressources en collections logiques, les développeurs peuvent créer des API intuitives, évolutives et conformes aux principes de REST.
Comprendre comment définir, identifier et manipuler les ressources à l'aide des méthodes HTTP standard est essentiel pour les concepteurs et les consommateurs d'API. Associé aux meilleures pratiques en matière de conception d'URI, à l'utilisation appropriée des codes d'état HTTP et à une approche réfléchie de la granularité des ressources, un modèle de ressources bien défini conduit à des API qui sont non seulement fonctionnelles, mais aussi un plaisir à utiliser. Alors que le paysage numérique continue d'évoluer, les principes de l'architecture orientée ressources resteront une pierre angulaire d'une communication efficace des services web.