Développer avec Create React App et une API Node.js

#tl;dr

Dans un environnement de développement, pour lancer dans le même temps votre application React et une API basée sur Node.js, vous pouvez imbriquer judicieusement les deux dépôts Git, puis utiliser un script NPM et quelques packages bien pratiques tels que concurrently et nodemon pour lancer les deux serveurs d'une seule commande. Pratique ! D'autant que pour contourner les restrictions d'accès liées à la politique de même origine, create-react-app permet le paramétrage d'un proxy pour vos requêtes API.

#Est-ce que ça me concerne ?

La généralisation des architectures dites "API first" répond à des impératifs humains et techniques très divers. En ce qui concerne l'organisation du travail des développeurs, c'est l'assurance de pouvoir scinder l'implémentation de l'accès aux données -aux ressources- d'une part, et le travail sur l'UI/UX, d'autre part. Un premier groupe peut concevoir une API robuste et proposer un "contrat" clair à l'équipe frontend qui accède aux données avec un référentiel unique, que l'application soit web ou mobile.

De cette façon, la conception de l'interface utilisateur est libérée d'une grande partie des contraintes qui régissent les architectures MVC traditionnelles. Le développeur peut ainsi mieux se concentrer sur la qualité de sa réponse aux spécifications fonctionnelles.

Si l'architecture de votre projet est de ce type, et que vous attaquez la conception d'un frontend SPA React avec create-react-app (quelle bonne idée !), ce qui suit peut vous éclairer. Nous allons voir comment il est possible d'accéder sans se compliquer la vie à une API RESTful basée sur Node.js, en imbriquant correctement ses dépôts.

#Deux dépôts : le frontend, l’API

Le principe est le suivant : vous ne souhaitez pas forcément modifier l'API qui est implémentée par une autre équipe, ou par un collègue, mais vous devez y accéder facilement depuis votre application React.

Vous allez pour cela devoir travailler sur deux dépôts Git clonés : celui du frontend React contiendra par exemple celui de l'API, et un script NPM se chargera de lancer les deux applications, sur deux ports différents.

Faut-il utiliser un framework en particulier pour le backend ?
Absolument pas ! Pour ma part je travaille plus volontiers avec LoopBack, mais tout ce que qui s'appuie sur Node.js fait l'affaire.

#Organisation locale du code

Mettons que votre projet React s'appelle my-react-frontend et que l'API qu'il consomme répond au doux nom de my-node-api.

my-react-frontend est cloné à la racine, c'est le projet parent. Il contient au moins les répertoires src/, public/ et node_modules/ générés par create-react-app.

build/ peut également être présent si vous avez déjà lancé au moins une fois la commande npm run build.

A la racine de my-react-frontend, clonez le dépôt my-node-api.

Vous devez obtenir :

my-react-frontend/
-- my-node-api/
-- node_modules/
-- public/
-- src/
...

Ne nous attardons pas trop sur my-node-api, qui peut être implémenté de très nombreuses manières. Partons du principe qu'une fois lancé, le serveur expose les ressources dont votre application a besoin sur http://localhost:3001. Et disons juste que si l'équipe backend vous signale une mise à jour, vous ferez simplement :

cd my-node-api/
git pull

Faut-il forcément organiser les dépôts de cette façon ?
Pas du tout. Mais l'intérêt de cette configuration, c'est que le backend est "dans sa bulle" et que les développeurs qui le font évoluer n'ont pas à organiser le code en fonction de ce frontend en particulier.

Dernière chose importante : pensez à ajouter my-node-api/ au fichier .gitignore du projet React. Il ne faudrait évidemment pas qu'il versionne le backend.

#Passez moi sur le CORS

En production, il est fréquent d'utiliser le même serveur pour servir l'application React et l'API sous-jacente. Dans cette configuration, le mécanisme de Cross-origin resource sharing (CORS), basé sur des headers HTTP, n'a pas à être implémenté.

En développement, par contre, il est plus pratique de dissocier les serveurs pour bénéficier de toutes les fonctionnalités de l'écosystème React.

Pour répondre à cette contrainte, create-react-app propose un mécanisme qui permet de mettre en place un proxy d'API.

En partant du principe que votre frontend écoute sur le port 3000, et le serveur API sur le port 3001, il suffit d'ajouter un paramètre au premier niveau du package.json :

{
  "proxy": "http://localhost:3001",
}

De cette façon, vous pourrez utiliser un chemin relatif pour accéder à vos ressources. Si une requête ne concerne pas un asset statique, elle sera relayée vers votre backend. fetch('/api/bananas'), par exemple, requêtera notre API sur http://localhost:3001/api/bananas.

#Tout lancer en une seule commande

Nous utiliserons pour cela un script NPM défini dans le package.json situé à la racine du projet React.

Deux petits outils seront nécessaires pour créer le script ad hoc :

  • le package concurrently qui permet de lancer plusieurs scripts en une seule commande. Faites par exemple un npm install --save-dev concurrently.
  • le package nodemon qui scrute votre backend Node.js et relance le serveur automatiquement en cas de modification du code. Faites donc un npm install --save-dev nodemon, vous ne le regretterez pas.

Tout est prêt ! Ouvrez package.json et ajoutez dans les scripts:

"start-with-api": "concurrently \"react-scripts start\" \"PORT=3001 nodemon ./my-node-api/server/server.js\""

Le chemin d'accès au script serveur est à adapter en fonction de vos propres choix techniques ! Notez que dans ce cas précis, on passe une variable d'environnement PORT que le script serveur utilise pour écraser son port d'écoute par défaut.

Au final, le package.json doit ressembler à ceci :

{
  "name": "my-react-frontend",
  "version": "0.1.0",
  "private": true,
  "proxy": "http://localhost:3001",
  "scripts": {
    "start": "react-scripts start",
    "start-with-api": "concurrently \"react-scripts start\" \"PORT=3001 nodemon ./my-node-api/server/server.js\"",
    "build": "react-scripts build",
    "test": "react-scripts test --env=jsdom",
    "eject": "react-scripts eject"
  },
  "devDependencies": {
    "react-scripts": "1.0.14",
    "concurrently": "3.5.0",
    "nodemon": "1.12.1"
  },
  "dependencies": {
    "react": "^16.0.0",
    "react-dom": "^16.0.0"
  }
}

Pour mémoire, nous n'avons ajouté que deux lignes : "proxy" et "scripts/start-with-api".

#Une astuce pour les pressés

Si le backend ne joue pas un grand rôle dans votre application ou si -plus probablement- vous souhaitez démarrer sans attendre que le véritable backend soit disponible, je vous conseille de tester l'excellent json-server.

Cet élégant package offre la possibilité de créer un fichier JSON avec quelques données factices (data fixtures) et de les mettre à disposition de votre application à la façon d'une API RESTful, grâce à un simple json-server --watch db.json.

Il va sans dire qu'en modifiant légèrement le script start-with-api, vous disposerez en quelques secondes d'un backend au poil pour votre nouvelle application.