Les bonnes pratiques à suivre pour développer des APIs REST

Les bonnes pratiques à suivre pour développer des APIs REST

150 150 Thibaut CHARLIER

Petit rappel de ce qu’est une API REST et de son utilité :

En 2000, Roy Fielding a proposé le REST (State Architectural Transfer) comme approche architecturale de la conception de services Web. REST est un style architectural destiné à la construction de systèmes distribués basés sur l’hypermédia. REST est indépendant de tout protocole sous-jacent et n’est pas nécessairement lié à HTTP. Cependant, la plupart des implémentations REST utilisent HTTP comme protocole d’application. Ce guide se concentre sur la conception d’API REST pour HTTP.

L’API REST est au développeur ce que l’interface utilisateur est à l’utilisateur final, une méthode de communication avec un serveur.

Le contexte suivi comme exemple dans ce document :

Pour donner un côté concret à nos bonnes pratiques, nous allons nous choisir un exemple de la vie réelle que nous allons transformer en API REST.
Imaginons qu’une société veuille gérer les appareils qu’elle confie à ses collaborateurs afin de savoir qui utilise quel appareil.

Commencez par réfléchir aux opérations que votre API va permettre :

Dans notre hypothèse de départ, nous pouvons imaginer les opérations suivantes :

Pour la gestion des personnes

  • Créer une personne
  • Récupérer la liste des personnes existantes
  • Récupérer les informations détaillées d’une personne (nous ne parlerons pas ici de l’aspect confidentialité des informations stockées ni de l’aspect RGPD)
  • Mettre à jour les informations concernant une personne
  • Supprimer une personne

Pour la gestion des appareils

  • Créer un appareil
  • Récupérer la liste des appareils
  • Récupérer les informations relatives à un appareil
  • Mettre à jour les informations relatives à un appareil
  • Supprimer un appareil

Pour la gestion de « qui à quoi »

  • Lier un utilisateur à un appareil
  • Supprimer le lien entre un utilisateur et un appareil

(Par soucis de facilité, nous n’allons pas gérer ici le fait de transférer l’attribution d’un appareil d’un utilisateur à un autre, il suffirait pour faire cette opération de commencer par supprimer le lien et ensuite de le recréer avec un nouvel utilisateur)

Voyons comment mettre en œuvre des APIs sur base de notre exemple de départ et regardons quelles sont les bonnes pratiques que nous pouvons en déduire.

Bonne Pratique n°1 – Utiliser les verbes HTTP :

Bien qu’il n’y ait aucune limitation technique, la règle communément admise pour l’utilisations des verbes http est la suivante :

HTTP VERB Equivalent SQL Description
GET select Lecture d’une information
POST insert Écrire une information
PUT update Mettre à jour une information
DELETE Delete Supprimer une information

Bonne Pratique n°2 – Utiliser des mots et pas de verbes dans votre URL :

 Prenons en exemple l’opération de récupérer tous les utilisateurs enregistrés.

La bonne pratique est de retourner la liste des utilisateurs pour une requête « GET » (pour rappel, nous lisons une information) pour l’url /users

VERB URL Description
GET /users Récupération des identifiants de chaque utilisateur.

A ne pas faire

VERB URL Description
GET /getallusers Récupération des identifiants de chaque utilisateur.
GET /allusers Récupération des identifiants de chaque utilisateur.

Sur base de cette première API, nous pouvons en déduire 2 nouvelles bonnes pratiques.

Bonne Pratique n°3 – Utiliser l’anglais pour nommer vos ressources :

L’informatique est un monde principalement anglophone, si vous souhaitez fournir des API universelles, la langue de Shakespeare s’impose d’elle-même.

 Bonne Pratique n°4 – Utiliser le pluriel pour nommer vos ressources :

Lorsque l’URL se rapporte à un groupe de ressources (ici nous parlons bien de plusieurs utilisateurs et non pas d’un seul), le pluriel s’impose pour indiquer aux développeurs que l’URL se rapporte à un « tableau » de ressources.

Bonne Pratique n°5 – Utiliser correctement les codes de retour :

Le protocole http nous offre une grande flexibilité dans les codes de retour que notre API peut envoyer en réponse, il faut les utiliser là où ils ont un sens.

Code de retour Description et utilisation
200 OK Le serveur à traiter la requête avec succès.
201 CREATED Une nouvelle ressource a été créée.
204 No Content Peut être utilisée en réponse à une requête DELETE effectuée avec succès.
206 Partial Content En réponse à une requête demandant une réponse trop lourde pour être envoyée en une seule fois. De la pagination va être nécessaire pour récupérer l’ensemble des informations
304 Not Modified Le client peut utiliser les données en cache car elles n’ont pas été modifiées depuis la date spécifiée.
400 Bad Request La requête est invalide et ne peut pas être traitée par le serveur.
401 Unauthorized La requête nécessite que le client soit identifié.
403 Forbidden Le serveur a compris la requête mais l’utilisateur n’est pas autorisé à accéder à cette API.
404 Not Found La ressource demandée n’existe pas.
500 Internal Server Error Votre code ne devrait jamais renvoyer cette erreur. Cette erreur devrait être récupérée par votre code et traitée, pour ensuite renvoyer une réponse adéquate au client.

Prenons l’exemple de la création d’un utilisateur, la requête devrait être un POST sur l’URL /users

VERB URL Code de retour Description
POST /users 201 Created Création d’un utilisateur

Bonne Pratique n°6 – Utiliser des sous-ressources pour identifier un élément unique :

Si vous souhaitez récupérer les informations relatives à un utilisateurs précis, votre requête devrait utiliser le verbe « GET » et l’URL devrait avoir la forme /users/{user-ID} où {user-ID} est l’ID de l’utilisateur spécifique.

VERB URL Code de retour Description
GET /users/{user-ID} 200 OK Récupération des informations spécifiques de l’utilisateur

 Bonne Pratique n°7 – Utiliser des sous-ressources pour identifier une relation :

Si vous souhaiter récupérer le liste des appareils liés à un utilisateur spécifique, votre requête utilisera le verbe « GET » et l’URL devrait avoir la forme /users/{user-ID}/devices.

VERB URL Code de retour Description
GET /users/{user-ID}/devices 200 OK Récupération des appareils liés à un utilisateur/.

De la même façon, si vous voulez lier un appareil à un utilisateur, la requête utilisera le verbe « POST » et l’URL aura la forme /users/{user-ID}/devices/{device-ID}. Dans ce cas, {user-ID} et {device-ID} sont respectivement l’ID de l’utilisateur qui reçoit l’appareil et l’ID de l’appareil qu’il reçoit.

VERB URL Code de retour Description
POST /users/{user-ID}/devices/{device-ID} 201 Created Création du lien entre l’utilisateur et le l’appareil.

Bonne Pratique n°8 – Spécifiez le format de vos données :

Il est important que le développeur (et l’application qu’il développe) qui consomme vos API connaisse le format de votre réponse. Le header Content-Type existe justement pour répondre à ce besoin. Utilisez-le toujours dans les réponses de vos APIs.

N’utilisez le Content-Type « text/plain » que si la réponse est effectivement du texte.

Le content type le plus utilisé, pour ne pas dire le content type standard des API REST est le JSON « application/json ».

Il est également très important de spécifier le « charset » que vous utilisez. Dans la majorité des cas, et sauf contre-indication, le charset que vous utilisez est le « UTF-8 ».

Le Header que vous envoyez en réponse aux requêtes clients devrait normalement être :

« Content-Type » :  « application/json ; charset =  utf-8 »

Bonne Pratique n°8 – La gestion des temps (date et heure) :

Le sujet peut sembler trivial mais il est loin de l’être.

En reprenant le sujet de la gestion de nos appareils et de nos utilisateurs, imaginons que nous souhaitions savoir depuis quand un utilisateur a à sa disposition un appareil spécifique.

L’appel que nous allons faire ressemblera à :

VERB URL Code de retour Description
GET /users/{user-ID}/devices/{device-ID} 200 OK Récupération des informations relatives à l’attribution d’un appareil spécifique pris en charge par un utilisateur spécifique.

La complexité vient du fait que l’attribution de l’appareil a peut-être été enregistrée par une personne dans une time zone X pour un utilisateur dans une time zone Y et doit être visualisée par un utilisateur dans une time zone Z. Comment faire en sorte que tout le monde voit le temps qui corresponde à sa time zone ? Une des solutions à ce problème est de fournir la date et l’heure sous format Timestamp UTC (Petit rappel : le timestamp est le nombre de secondes écoulées depuis le 1er janvier 1970 / L’heure UTC (Universal Time Coordinated), en français Temps Universel Coordonné, est l’heure de référence internationale. Elle correspond aussi à l’heure GMT (Greenwich Mean Time) et à l’heure Z (Zulu)).

La réponse à ce call pourrait ressembler à :

{

            « attribution » : « enable »,

            « timeFrom » : 1557080067

}

Bonne Pratique n°9 – Utiliser HATEOAS (Hypermedia as the Engine of Application State)

Derrière cet acronyme se cache simplement le fait d’utiliser des liens hypertext dans les réponses de vos APIs pour indiquer au client de vos APIs où il peut aller chercher plus d’informations concernant une ressource.

L’exemple ci-dessous est un exemple de réponse à la requête qui permet de retrouver quels appareils sont liés à quel utilisateur. Dans la réponse ci-dessous on peut voir que l’appareil avec l’ID 123456 est lié à l’utilisateur avec l’ID « abcde ». La réponse ajoute que des informations relatives à l’appareil (« rel » : « self ») peuvent être trouvées à l’URL spécifiée dans le lien HREF.

{

            « user-id »: »abcde »,

            « devices »:[

                        {

                                   « device-ID » : 123456,

                                   « links »:{

                                               « rel »: »self »,

                                               « href »: »/devices/123456″

                                   },{

                                               « rel »: »list »,

                                               « href »: »/devices »

                                   }

                        }

            ]

}

Bonne Pratique n°10 – Proposer des filtres, du tri, de la pagination pour les collections

L’utilisabilité de votre API pourra être augmentée par l’ajout de fonctionnalités telles que les filtres, le tri ou la pagination. Si l’on compare les filtres, tri ou pagination au langage SQL, les filtres correspondent à la clause « where », le tri correspond à la clause « sort by » et la pagination correspond aux clauses « limit offset ».

En reprenant notre exemple, si vous souhaitez ne récupérer que la liste des utilisateurs masculins, la requête aura la forme suivante :

VERB URL
GET /users?sex=m

Pour ajouter de la pagination, si vous ne souhaitez récupérer que les 10 premiers utilisateurs, la requête aura alors la forme suivante

VERB URL
GET /users?sex=m&limit=10

« limit » donne le nombre d’objets retournés à partir du début de la liste.

Pour récupérer les 10 utilisateurs suivants, la requête aura alors la forme suivante :

VERB URL
GET /users?sex=m&offset=10&limit=10

“offset” donne le point de départ du curseur à partir duquel on va prendre en compte les objets.

Et enfin, si vous souhaitez trier la réponse, la requête aura alors la forme suivante :

VERB URL
GET /users?sex=m&offset=10&limit=10&sort=+lastName,-birthDate

Dans ce cas-ci, le filtre se fera sur le nom de famille (en ordre alphabétique grâce au « + » indiqué devant « lastName » et ensuite le filtre se fera sur la date de naissance (en ordre décroissant grâce au « – » indiqué devant le « birthDate ».

Bonne Pratique n°11 – Documentez votre API

Si vous souhaitez que votre API soit facilement utilisable et que les développeurs qui auront à la consommer ne vous fagocitent pas votre temps pour comprendre comment l’utiliser, il est INDISPENSABLE de fournir une documentation claire et précise.

Vous aurez donc à documenter :

– les différents chemins à utiliser et leur construction

– Pour chaque chemin, les différentes méthodes (VERB) disponibles

– Pour chaque méthode :

            – Les paramètres acceptés et leur type

            – le format accepté

            – le modèle du body s’il est utilisé

– Les headers acceptés

– La/les méthodes d’authentification

Au-delà des descriptions techniques, vous devriez également indiquer aux développeurs votre politique d’utilisation de votre API. Avez-vous un quota d’utilisation (de call) à respecter/à ne pas dépasser, une « Fair Use Policy »,… ?

Bonne Pratique n°12 – Versionnez votre API

Comme toute solution informatique, votre API devrait connaître des évolutions, et donc des versions. Il est important d’indiquer aux développeurs qui consomment votre API la façon dont ils peuvent spécifier la version de l’API qu’ils appellent.

Une méthode largement utilisée est d’indiquer le numéro de version dans le PATH de l’URL.  Cette méthode a l’avantage d’être simple à mettre en œuvre de votre côté mais l’est peut-être un peu moins pour vos clients (les développeurs qui consomme votre API). En effet, s’il souhaite faire certains appels sur une version N, et d’autres appels sur une version N+1, ils vont devoir gérer deux préfixes de chemins différents.

Une autre solution consiste à faire passer le numéro de version des APIs en paramètres, ou ce qui se fait plus largement, en Header à la requête. Un des avantages de cette solution est de permettre d’avoir une version courante qui évolue au fil du temps. En effet, si le développeur n’utilise pas de paramètre ou de header pour spécifier la version, cela implique qu’il utilise de facto la version courante que vous proposez. Cette technique, bien que séduisante, cache un piège non négligeable, vos versions d’API doivent être compatibles entre elles pour que le développeur qui ne fait pas de modification à son application et appel continuellement la version courante, ne soit pas mis en défaut lorsque vous ferez une update de la version courante. De la version N à la version N+1. Attention, cette solution vous donnera également plus de travail de gestion.

Pour reprendre notre exemple, nous pourrions avoir les solutions suivantes pour récupérer une liste d’utilisateurs :

VERB URL
GET /v1/users
GET /users?version=1
GET /users

Headers: “version:1”

Bonne Pratique n°12 – Décorrelez les attributs des réponses avec les attributs de votre Data Store.

D’un point de vue sécurité, il est bon de ne pas exposer directement le nom des champs de vos bases de données (ou des attributs si l’on parle NoSQL) directement dans les réponses de votre API.  En effet, si vous exposez en direct le nom des champs ou des attributs dans vos réponses, vous facilitez le travail des pirates qui auraient envie de vous attaquer. Il est donc important d’avoir une table de mapping entre les informations que vous récupérez depuis votre Data Store (SQL, NoSQL,…) et les informations que votre API envoie en réponse.

Pour revenir à notre exemple, si l’API qui reçoit les coordonnées d’une personne doit envoyer son nom, son prénom, sa date de naissance, on peut imaginer que le retour de ce call-là aura en Body un JSON qui devrait avoir ressembler à ce que l’on trouve ci-dessous :

{

            « user » :{

                        « firstName » : « Thibaut »,

                        « lastName » : « CHARLIER »,

                        « birthDate » : 123984000

}

}

Bonne Pratique n°13 – Pensez aux applications qui vont consommer vos APIs

En construisant les différents chemins de vos APIs, pensez aux développeurs qui vont devoir les consommer et aux performances de votre Backend si trop de requêtes http sont nécessaires pour remonter une seule donnée.

En reprenant notre exemple initial, une requête en GET sur le chemin /users devrait retourner uniquement une liste de userID, qui permettrait ensuite de retrouver les informations des utilisateurs en faisant une requête GET sur le chemin /users/{user-ID}. Même si cette solution peut sembler pertinente, si une application veut uniquement afficher le prénom de chaque utilisateur, cela implique que cette application fasse un premier call pour retrouver la liste des user-ID et ensuite un call pour chaque utilisateur afin de retrouver son prénom, vous multipliez donc énormément le nombre de requêtes nécessaires.

Il peut donc parfois être nécessaire de se montrer flexible sur les réponses que votre API reçoit afin de préserver autant les performances et les coûts de votre plateforme que les performances des applications qui consomment vos APIs.

Chez Gekko, nous nous efforçons de respecter ces bonnes pratiques lorsque nous construisons des APIs (autant pour des développements interne que pour nos clients) tout en nous montrant souple pour coller au mieux aux besoins applicatifs.

Laisser une réponse