1. Ajouter un nouveau pokémon

Pour mettre en place les premières « vraies » interactions dans notre API Rest de pokémons, quoi de mieux que de commencer par l’ajout d’un nouveau pokémon, et ainsi agrandir la liste des pokémons disponibles ?

Le seul petit bémol, c’est que nous n’avons aucune idée de comment réaliser cela avec Express pour le moment…

Alors, reprenons les choses dans l’ordre. Nous devons construire un point de terminaison qui acceptera la requête HTTP suivante :

  • Le type d’action HTTP : L’action permettant d’ajouter une nouvelle ressource est POST, comme nous avons déjà eu l’occasion de le voir.
  • L’URL de la ressource : C’est l’emplacement de la ressource sur laquelle nous souhaitons intervenir, c’est-à-dire son l’URL. Dans notre cas, nous voulons ajouter un élément à la collection de ressources des pokémons, ou ressources pluriels. Concrètement, il s’agit de /api/pokemons.
  • Les données du pokémon : Il s’agit des informations du nouveau pokémon que l’on souhaite ajouter à l’API Rest, au format JSON.

On va donc mettre en place ce nouveau point de terminaison dans le point d’entrée app.js :

//...
 
app.post('/api/pokemons', (req, res) => {
  const id = 123
  const pokemonCreated = { ...req.body, ...{id: id, created: new Date()}}
  pokemons.push(pokemonCreated)
  const message = `Le pokémon ${pokemonCreated.name} a bien été crée.`
  res.json(success(message, pokemonCreated))
})
 
// ...

On va détailler ce code ensemble ligne par ligne, car il recèle une petite subtilité et certaines parties de la syntaxe ne sont pas évidentes non plus !

Voici donc les explications :

  • À la ligne 3, on définit l’action POST auprès d’Express, ainsi que l’URL associée.
  • Ensuite, à la ligne 4, juste en dessous, on définit un identifiant arbitrairement… et oui, je ne savais pas quoi mettre ! Les identifiants des pokémons doivent être uniques, afin de générer une URL unique pour chacun d’entre eux, là-dessus on est d’accord. Par contre, côté API Rest, comment fait-on pour générer un nouvel identifiant unique, puisque par définition on ne peut pas « deviner » un identifiant qui ne soit pas déjà pris… Donc comment fait-on ? C’est un problème récurrent lorsqu’on souhaite ajouter une nouvelle ressource : c’est à la base de données de déterminer les identifiants uniques, car elle seule a accès à l’ensemble des pokémons existants. On verra cela plus tard donc…
  • Puis à la ligne 5, on fusionne les données du pokémon reçues via la requête HTTP entrante, et l’identifiant unique que l’on a généré, même si ce dernier n’est pas très fiable. De plus, on en profite pour ajouter au pokémon la date de création du pokémon. Comme chaque client peut avoir une date interne différente, c’est mieux qu’une source unique comme le serveur attribue la date de création à tous les pokémons. Il nous reste ensuite à ajouter ce nouveau pokémon « complet » à la liste des pokémons existants, à la ligne 6.
  • Pour terminer, on génère un message de confirmation pour les consommateurs de notre API Rest, et on retourne l’ensemble dans une belle réponse JSON. 👍

Et le tour est joué !

Oulà, tu t’emballes là ! C’est quoi cet identifiant « unique ». Et si on ajoute deux pokémons à la suite ? Ils auront le même identifiant !

Ah oui, c’est vrai qu’on est allé un peu vite en besogne.

En fait, on doit générer un identifiant unique, c’est-à-dire différent de tous les identifiants déjà existants. Pour cela, je vous propose une solution simple et fiable.

On va récupérer l’identifiant le plus grand parmi la liste existante et l’incrémenter de la valeur 1. Ainsi, on est sûr de récupérer un identifiant unique pour chaque nouveau pokémon que l’on ajoute dans notre API Rest.

Nous allons ajouter cette méthode de génération d’identifiant dans le fichier helper.js, car il s’agit d’une méthode « outil » :

exports.success = (message, data) => {
  return { message, data }
}
 
exports.getUniqueId = (pokemons) => {
  const pokemonsIds = pokemons.map(pokemon => pokemon.id)
  const maxId = pokemonsIds.reduce((a,b) => Math.max(a, b))
  const uniqueId = maxId + 1
   
  return uniqueId
}

Dans ce code, on commence par transformer le tableau des pokémons en un tableau d’identifiant des pokémons, contenu dans la constante pokemonsIds. Cela est possible grâce à la méthode JavaScript map, qui fonctionne comme une boucle for, mais en retournant un nouveau tableau.

Ensuite, on calcule l’identifiant existant le plus grand parmi la liste des identifiants de pokémons. Ce prodige est possible grâce à la méthode JavaScript native reduce, qui permet de comparer les éléments deux à deux dans un tableau. Bref, retenez que cette méthode nous retourne la constante maxId, qui est ce qui nous intéresse principalement.

Pour finir proprement, il ne nous reste plus qu’à retourner l’identifiant unique du nouveau pokémon, en incrémentant la valeur de la constante précédente maxId de 1.

Il nous reste ensuite à utiliser cette nouvelle méthode outil pour générer un identifiant unique lors de l’ajout d’un nouveau pokémon, plutôt que d’écrire « 123 » en dur, dans le point d’entrée app.js :

// ...
const { success, getUniqueId } = require('./helper.js');
 
//...
 
app.post('/api/pokemons', (req, res) => {
  const id = getUniqueId(pokemons)
  const pokemonCreated = { ...req.body, ...{id: id, created: new Date()}};
  pokemons.push(pokemonCreated)
  const message = `Le pokémon ${pokemonCreated.name} a bien été crée.`
  res.json(success(message, pokemonCreated))
})
 
// ...

Nous voilà désormais avec un point de terminaison permettant d’ajouter un nouveau pokémon… prêt à l’emploi !

Le léger problème que nous avons encore, c’est que nous avons aucune idée de comment tester ce point de terminaison. On ne peut pas utiliser notre navigateur pour effectuer des requêtes de type POST. Nous allons donc devoir trouver un autre moyen.

Mais rassurez-vous, on va bien trouver une solution. 😇

Idéalement, seul la base de données est responsable d’attribuer un identifiant unique. Pour le moment on le garantira à la main depuis notre API Rest, mais plus tard c’est bien MySQL qui s’en chargera.