Lorsque l’on développer une nouvelle application React, on a rarement un backend à notre disposition, pour pouvoir effectuer tout nos tests. Et pourtant, une application frontend qui ne communique avec aucune API Rest… ce n’est pas très utile, pas vrai ? J’ai également été dans cette situation il n’y a pas si longtemps, et je me suis demandé comment les autres développeurs testés leurs applications, car nous n’avons pas tous à disposition un backend prêt-à-l’emploie.

Bien sûr, nous n’allons pas nous laisser faire. En tant que développeurn, nous avons l’habitude de nous débrouiller avec les moyens du bord, et nous allons voir comment simuler une API Rest dans notre application…

Ce que nous aimerions faire, c’est communiquer avec un serveur distant pour récupérer des données, les éditer et sauvegarder les changements sur le serveur, et pourquoi pas en ajouter ou en supprimer. Bref, que vous sachiez effectuer toutes les opérations HTTP CRUD standards, depuis votre application React. C’est parti ?

1. Simuler une API Rest

Jusqu’à maintenant, vous deviez surement stocker et récupérer vos données depuis des constantes ou des services écrits « en dur » dans votre projet. Seulement, ce système ne nous convient plus, et nous voulons pouvoir communiquer avec un serveur distant désormais !

Malheureusement, nous ne disposons pas d’un serveur pour gérer les requêtes de notre application React JS…

Mais alors, comment va-t-on faire ? 🥺

Je vous rassure, en tant que développeur, on a l’habitude de se débrouiller. On va utiliser une petite librairie qui nous permet de simuler une API Rest, qui fonctionnera comme si nous utilisions un serveur distant ! Cette simulation est possible grâce à la librairie json-server, qui est une des librairies les plus populaires pour mettre en place une petite API Rest sans trop d’efforts. Nous allons voir comment cela fonctionne.

D’abord, installez cette librairie miraculeuse avec la commande suivante :

npm install -g json-server

L’option -g permet d’installer cette librairie au niveau global sur votre poste de travail. Ainsi, vous pourrez réutiliser cette librairie entre vos différents projets.

Il est nécessaire de lancer cette commande en tant qu’administrateur sur votre poste de travail. Si vous êtes sur Windows, « Clic droit > Exécuter en tant qu’administrateur ». Si vous êtes sur Mac ou Linux, préfixez la commande par « sudo « . Ensuite ça devrait mieux fonctionner. 👍

Maintenant, nous avons la possibilité de simuler une API Rest. Cependant, il va bien falloir sauvegarder nos données quelque part. Pour cela, la librairie json-server utilise un fichier dans votre projet, afin de sauvegarder tous vos données et les éventuelles modifications que les utilisateurs pourraient apporter dessus.

On va donc créer une mini base de données, sous la forme d’un fichier. Créez donc un nouveau fichier db.json dans votre projet. J’ai pré-remplis la base de données avec les information de douze pokémons (pourquoi pas ?):

{
  "pokemons": [
    {
      "id": 1,
      "name": "Bulbizarre",
      "hp": 25,
      "cp": 5,
      "picture": "https://assets.pokemon.com/assets/cms2/img/pokedex/detail/001.png",
      "types": ["Plante", "Poison"]
     },
     {
      "id": 2,
      "name": "Salamèche",
      "hp": 28,
      "cp": 6,
      "picture": "https://assets.pokemon.com/assets/cms2/img/pokedex/detail/004.png",
      "types": ["Feu"]
     },
     {
      "id": 3,
      "name": "Carapuce",
      "hp": 21,
      "cp": 4,
      "picture": "https://assets.pokemon.com/assets/cms2/img/pokedex/detail/007.png",
      "types": ["Eau"]
     },
     {
      "id": 4,
      "name": "Aspicot",
      "hp": 16,
      "cp": 2,
      "picture": "https://assets.pokemon.com/assets/cms2/img/pokedex/detail/013.png",
      "types": ["Insecte", "Poison"]
     },
     {
      "id": 5,
      "name": "Roucool",
      "hp": 30,
      "cp": 7,
      "picture": "https://assets.pokemon.com/assets/cms2/img/pokedex/detail/016.png",
      "types": ["Normal", "Vol"]
     },
     {
      "id": 6,
      "name": "Rattata",
      "hp": 18,
      "cp": 6,
      "picture": "https://assets.pokemon.com/assets/cms2/img/pokedex/detail/019.png",
      "types": ["Normal"]
     },
     {
      "id": 7,
      "name": "Piafabec",
      "hp": 14,
      "cp": 5,
      "picture": "https://assets.pokemon.com/assets/cms2/img/pokedex/detail/021.png",
      "types": ["Normal", "Vol"]
     },
     {
      "id": 8,
      "name": "Abo",
      "hp": 16,
      "cp": 4,
      "picture": "https://assets.pokemon.com/assets/cms2/img/pokedex/detail/023.png",
      "types": ["Poison"]
     },
     {
      "id": 9,
      "name": "Pikachu",
      "hp": 21,
      "cp": 7,
      "picture": "https://assets.pokemon.com/assets/cms2/img/pokedex/detail/025.png",
      "types": ["Electrik"]
     },
     {
      "id": 10,
      "name": "Sabelette",
      "hp": 19,
      "cp": 3,
      "picture": "https://assets.pokemon.com/assets/cms2/img/pokedex/detail/027.png",
      "types": ["Normal"]
     },
     {
      "id": 11,
      "name": "Mélofée",
      "hp": 25,
      "cp": 5,
      "picture": "https://assets.pokemon.com/assets/cms2/img/pokedex/detail/035.png",
      "types": ["Fée"]
     },
     {
      "id": 12,
      "name": "Groupix",
      "hp": 17,
      "cp": 8,
      "picture": "https://assets.pokemon.com/assets/cms2/img/pokedex/detail/037.png",
      "types": ["Feu"]
     }
  ]
}

Contrairement aux apparences, ce fichier n’est pas un simple objet statique. Il va servir de base de données pour json-server, et c’est ce fichier qui contiendra les dernières données à jour de votre application.

De son côté, json-server met en place plusieurs points de terminaisons sur lesquels nous pourrons effectuer des requêtes :

// Exemple avec une collection de pokémons :
GET /pokemons // Renvoie tous les pokémons 
GET /pokemons/1 // Retourne le pokémon avec l'identifiant 1 
POST /pokemons // Ajoute un nouveau pokémon dans notre application
PUT /pokemons/1 // Modifie le pokémon avec l'identifiant 1 
  
// Cette requête permet de retourner certaines pokémons en fonction d'un terme de recherche :
GET api/pokemons?q=term // 'term' est le terme de recherche.

Bien pratique n’est-ce pas ? Il est quasiment possible de faire toutes les requêtes possibles pour une petite application de démonstration : ajout, suppression, modification, recherche, etc. Nous allons voir prochainement comment faire tout cela.

Nous verrons également comment utiliser cette API, plutôt que les données « en dur » du fichier mock-pokemons.ts, depuis nos composants.

Mais pour le moment, notre API Rest n’est pas encore prête à être utilisé, il faut encore la démarrer !

2. Démarrer notre API Rest

Nous devons maintenant démarrer notre API Rest, afin de pouvoir ajouter et modifier des données dans notre petite base de données. Pour cela, la librairie json-server met à notre disposition la commande suivante :

json-server --watch src/models/db.json --port=3001

Cette commande prend deux paramètres : watch et port. Le premier paramètre permet d’indiquer l’emplacement de notre fichier qui sert de base de données, nommée db.json dans notre cas. Ensuite, on indique le port dans lequel on veut démarrer notre API Rest. On précise « 3001 » dans notre cas, car il est probable que application React tourne déjà sur un autre port (3000 ou 4200 généralement). 💡

Mais cette commande est peu longue non, on va devoir écrire tout ça à chaque fois qu’on veut démarrer une API ? 🤔

Effectivement, cela peut vite devenir pénible. À la place, on va définir une nouvelle instruction dans notre fichier package.json :

{
  "name": "my-awesome-reactjs-app",
  ...
  "dependencies": {
    ...
  },
  "scripts": {
    "start:api": "json-server --watch src/models/db.json --port=3001",
    "start": "react-scripts start",
    "build": "react-scripts build"
  },
  ...
  }
}

À la ligne 8, on définit un nouveau script start:api, qui sera chargé de démarrer notre API Rest simulée. Dans un terminal, lancez la commande suivante désormais :

npm run start:api

Si vous voyez un message similaire à celui-ci s’afficher dans votre navigateur, alors vous êtes sur la bonne voie :

Notre API Rest semble avoir démarré correctement.

Ensuite, retournez dans votre navigateur, et ouvrez un nouvel onglet. Nous allons tester que notre API est bien démarré correctement.

Saisissez l’adresse suivante :

http://localhost:3001/pokemons/1

Et vérifiez que votre navigateur vous renvoie l’écran suivant :

Voici le pokémon « Bulbizarre », du point de vue du serveur !

Je vous présente le premier pokémon de notre base de donnée, Bulbizarre, du point de vue de notre serveur. Il s’agit simplement d’un objet JSON, tout simplement !

Mais avec ces quelques informations, nous pourrons ensuite construire une très belle interface pour nos utilisateurs. 🙂

Notre API Rest est correctement démarré sur notre poste de travail, on peut passer à la suite, c’est-à-dire utiliser cette API Rest depuis notre application de pokémons.

Pour la suite des développements, pensez à bien démarrer votre application React, et en plus votre API Rest. Concrètement, cela signifie ouvrir deux terminals, et lancez les commandes npm start et npm run start:api.

3. Consommer une API Rest

Nous avons mis en place une API Rest fonctionnelle sur notre machine locale, mais il nous reste maintenant à utiliser tout cela depuis notre application React JS. Nous allons commencer par récupérer les pokémons depuis notre API Rest.

Je vous propose d’imaginer un composant React JS qui affichera la liste de tous les pokémons contneus dans notre base de données. La route du côté de notre API Rest qui permet de récupérer tous les pokémons est : /pokemons. C’est une requête de récupération de ressources, donc le type de la requête HTTP à envoyer est GET.

Mais… comment faire cette requête HTTP ?

Qu’est ce qu’il nous faut exactement… Et bien j’ai une bonne nouvelle, nous n’avons besoin de rien ! 😉

Depuis quelques temps, les navigateurs et JavaScript supportent nativement une nouvelle spécification nommée Fetch API. Cette spécification fournit une interface JavaScript pour exécuter et traiter des requêtes HTTP de manière simple. Mais surtout, cette spécification fournit une méthode globale nommée fetch qui permet d’envoyer des requêtes de manière asynchrone. Cela signifie que l’on peut utilisé la méthode fetch() n’importe où dans notre code, sans rien importer. 🔥

De plus, la méthode fetch nous renverra une promesse, ou Promise en anglais. Pour rappel, les promesses sont natives en JavaScript depuis l’arrivée d’ES6, qui permet de faciliter les traitements asynchrones dans votre code. Mais voyons comment tout cela fonctionne.

Dans votre composant pokemon-list.tsx, remplacez le code du Hook d’effet comme ceci :

// Les importations.
 
const PokemonList: FunctionComponent = () => {
  ...
 
  useEffect(() => {
    // GET Request
    fetch('http://localhost:3001/pokemons') // On passe une url de récupération des pokémons à fetch().
    .then(response => response.json()) // On reçoit un objet Response de la Fetch API.
    .then((pokemons) => { // On récupère les pokémons grâce à la méthode json() précédente.
      setPokemons(pokemons); // On passe les pokémons à l'état de notre composant.
    });
  }, []);
 
  return (
    ...
  );
}
 
export default PokemonList;

Le code qui nous intéresse se trouve de la ligne 8 à 12. En quelques lignes, on récupère des pokémons du serveur et on les place bien au chaud dans le state de notre composant. Mais détaillons point par point.

D’abord à la ligne 8, on effectue une requête de type GET vers l’url http://localhost:3001/pokemons. Comment fait-on cela ? C’est on ne peu plus simple, voici la syntaxe : fetch(url). Et c’est tout !

Ensuite à la ligne 9, on récupère un objet response de la part de notre méthode préférée fetch. Cet objet n’est pas un simple objet JavaScript, mais un objet de type Response spécifiée par la Fetch API. Mais il y a plein d’informations dans cet objet qui ne nous intéresse pas forcément : des en-tête de la réponse HTTP, son statut, ou encore son url, etc. Bref, pour récupérer uniquement ce qui nous intéresse, à savoir les données de nos pokémons, on applique la méthode json sur cette réponse, afin d’extraire ces données.

Enfin, à ligne 20, on a enfin à disposition nos pokémons, et on les place dans le state de notre composant à la ligne 21. Le DOM virtuel s’occupera pour nous d’afficher ces pokémons à nos utilisateurs.

Si vous avez compris ce principe, alors vous êtes prêt à faire de même pour récupérer les informations d’un seul pokémon. L’’url de notre API Rest a appelé est /pokemons/<id> :

// On récupère un seul pokémon, grâce à son identifiant :
fetch(`http://localhost:3001/pokemons/${id}`)
 .then(response => response.json())
 .then(pokemon => {
  if(pokemon.id) setPokemon(pokemon);
});

Comme vous pouvez le constater, le code de l’envoie de la requête est quasiment identique, mise à part l’url appelée à la ligne 2. Aussi, il y a une petite bizarrerie à la ligne 5. Nous avons mis en place une condition sur l’identifiant du pokémon. Pourquoi une telle vérification… Et bien, c’est dû au fait que l’utilisateur pourrait demander l’identifiant d’un pokémon qui n’existe pas dans notre application, par exemple « 99 ».

Essayez d’accéder à l’url http://localhost:3001/pokemons/99 dans votre navigateur. Vous serez surpris de la réponse de notre API Rest, car vous recevrez un objet vide ! Mais c’est parfaitement normal, car dans notre fichier db.json il n’y a aucun pokémon avec l’identifiant « 99 ». Du coup, notre API Rest nous renvoie un objet vide, qui signifie en fait : Ok, j’ai regardé le pokémon que tu m’as demandé, mais il n’existe pas! Du coup, je te renvoie un objet vide, et tu devras de dérouiller avec ça.

Ce qu’on fait donc à la ligne 5, c’est vérifier si le pokémon reçu depuis le serveur a une propriété « id ». Si c’est le cas, alors on place ce pokémon dans le state de notre composant. Sinon, et bien nous ne faisons rien, car ce n’est pas un pokémon mais un objet vide. On pourrait alors afficher à nos utilisateurs « Aucun pokémon à afficher ». Parfait, on gère tous les cas possibles !

Bilan

Et voilà ! Mine de rien, vous savez désormais mettre en place une petite API Rest pour votre application React JS, et interagir avec grâce à la Fetch API. Et le mieux dans tout ça, c’est que cela ne nous a pas demandé trop de travail non plus.

Notre système est assez minimal, mais l’idée était de mettre les pieds dans le plat, et de vous donner les clefs pour à améliorer votre prochaine application React JS.

Maintenant, vous devriez avoir plus confiance en vous, et envisager l’avenir plus sereinement ! Félicitations, vous pouvez être fier de vous. 😎


Si vous avez besoin de consolider vos connaissances sur React JS pour votre travail, vos études ou vos projets personnels, je vous invite à me rejoindre pour une semaine complète avec React JS. Nous apprendrons React JS de zéro, en se basant sur les connaissances indispensables qu’ils manquent dans beaucoup de tutoriels : Les Composants Web, TypeScript ou encore ECMAScript 6.

Laisser un commentaire

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.