Quixo en React isomorphic avec Next.js

Pierre Haller
Pierre HallerJanuary 15, 2020
#integration#js#react

Cet article relate ma dernière semaine d'intégration chez Marmelab.

La semaine dernière, j'ai développé une application mobile grâce à React-native ainsi qu'une API en Node.js. Cette semaine, mon objectif est de pouvoir jouer depuis un navigateur web avec un joueur sur son mobile. Cette application devra être déployée sur internet. Pour cela j'aurai deux contraintes :

  • Elle devra être réalisée en React
  • Le premier rendu devra être côté serveur. C'est à dire qu'un utilisateur ayant désactivé javascript devra pouvoir se rendre sur l'application et voir le jeu, sans pour autant pouvoir jouer.

L'appli React isomorphic

SPA, Isomorphic & SSR : Un peu d'explications

React permet de construire des Single Page Application (SPA) ou Application web monopage en Français. L'application est chargée entièrement lors de la première requête, cela permet de ne pas recharger la page lorsque l'utilisateur navigue sur le site.

Une application en JS isomorphique signifie que le code peut être exécuté côté serveur ou côté navigateur. C'est indispensable pour faire du Server Side Rendering (SSR).

Le Server Side Rendering (ou Rendu Côté Serveur en Français) présente deux avantages : le référencement et la performance. Comme son nom l'indique, le rendu est effectué côté serveur plutôt que dans le navigateur de l'utilisateur. Cela permet d'avoir un premier affichage plus rapide, en particulier sur les appareils disposant d'une connexion lente. Voici un tableau comparant les différents types de rendus pour une application Web et les frameworks disponibles :

Tableau de comparaison de rendus

Vous pouvez trouver la source sur le site développeurs de google : Rendering on the web.

Si vous voulez plus d'infos sur le SSR ou React Isomorphic, il existe déjà de bons articles sur le blog de Marmelab :

Quel type de rendu, quel Framework ?

Rappelons l'objectif de cette semaine : pouvoir jouer à Quixo avec le premier rendu en SSR ! La première étape est donc de choisir quel type de rendu je souhaite avoir pour mon application. En regardant le tableau ci-dessus de la droite vers la gauche :

  • Full CSR : Pas de rendu côté serveur donc ne répond pas à la problématique.
  • CSR with Prerendering : Ne correspond pas à notre utilisation, il génére toutes les pages en HTML au build et les sert ensuite. À utiliser pour un site statique.
  • SSR with Rehydratation : Le serveur génére le HTML puis injecte le javascript dans la page.
  • Static SSR : Le serveur génère le HTML mais supprime le javascript, on n'est donc plus sur une SPA.
  • Server Rendering : Toutes les pages sont rendues côté serveur.

Je souhaite garder les avantages d'une SPA pour mon application. C'est-à-dire qu'uniquement le premier rendu devra être effectué côté serveur. Ensuite, l'utilisateur peut jouer à quixo sans que la page ne recharge. Pour ça, je me tourne vers le SSR avec rehydratation.

Next.js

Next.js permet justement de développer ce genre d'application rapidement et simplement ! Je décide donc d'utiliser ce framework.

J'ai été surpris par la rapidité avec laquelle j'ai pu commencer à développer grâce à Next.js ! Un simple npx create-next-app et j'ai une application qui tourne avec :

  • Le hot code reloading
  • Un router basé sur le filesystem
  • Rendu universel
  • Babel & Webpack configurés

Le développement

La seule différence avec l'application React-native que j'ai développée la semaine dernière est le rendu côté serveur. Pour cela, Next.js s'occupe de tout, ou presque ! Lors de la première requête, le rendu est effectué côté serveur automatiquement. Je n'ai qu'à ajouter une méthode à mon composant React pour lui dicter son comportement lors du premier rendu. Cette méthode est appelée getInitialProps. Par exemple, voici cette fonction pour ma page de jeu :

Game.getInitialProps = async ({ query, res }) => {
  const { id } = query;
  if (!id) {
    return createGameAndRedirect(res);
  }
  const game = await getExistingGame(id);
  const { team } = await getMyTeam(game.id);
  return { initGame: game, myTeam: team };
};

Cependant, cette méthode peut-être appelé côté serveur, si l'utilisateur arrive directement sur cette page, ou sur le navigateur du client, s'il est arrivé par un lien du site. Il faut donc adapter le code pour prendre ca en compte.

Par exemple, pour la méthode createGameAndRedirect qui est appelée lors de la création d'une nouvelle partie. La redirection n'est pas effectuée de la même manière si l'on est côté serveur ou côté navigateur. Pour savoir si le code est exécuté sur le serveur rien de plus simple : La réponse (res ci-dessus) est définie uniquement côté serveur. Voici donc le code de createGameAndRedirect :

const createGameAndRedirect = async res => {
  const game = await getNewGame();
  const href = `/game?id=${game.id}`;
  if (res) {
    res.writeHead(302, {
      Location: href
    });
    res.end();
  } else {
    Router.push(href);
  }
  return game;
};

On peut voir que si la réponse existe, on effectue une redirection http, sinon on utilise le router Next !

Le code étant, à peu de choses près, le même que celui de l'appli React-native, le résultat est presque identique : Résultat appli next.js

Retour Next.js

J'ai vraiment été séduit par Next.js, et surtout par la facilité d'installation et d'utilisation ! En plus, bien que tout soit configuré automatiquement lors du script d'initialisation (notamment Babel et Webpack), ça reste facilement surchargeable. J'espère pouvoir re-travailler sur ce framework pour me faire un meilleur avis sur la question, mais j'en ai eu une très bonne première impression.

Le déploiement

La dernière étape de mon intégration chez Marmelab est arrivée ! Je dois déployer mon application Next.js et l'API nodejs. C'est une première pour moi ! Heureusement mes collègues sont dispo pour m'aider !

Déploiement de l'appli Next.js

Pour déployer l'application Next.js, je décide de tester l'outil now.sh développé par le créateur de Next.js : Zeit. Une fois de plus, je suis impressionné par la facilité du fonctionnement de l'outil ! Voici les étapes que j'ai dû réaliser pour déployer l'application Next.js :

  • Créer un compte sur zeit
  • Installer now : npm i -g now
  • Me connecter sur now : now login
  • Déployer : now
Attention, installer des paquets globalement avec npm (-g) est souvent une mauvaise pratique. À n'utiliser que lorsque vous souhaitez installer quelque chose que vous utiliserez ensuite en dehors de votre projet. Comme ici où j'utilise now en ligne de commande.

Et voilà ! Mon application est déployée et accessible ! J'ai également accès aux logs depuis le dashboard fourni par Zeit : Dashboard Zeit

Il ne me reste plus qu'à déployer l'API pour pouvoir jouer !

Déploiement de l'API

Pour l'API, je vais utiliser AWS car Marmelab fournit un compte à tous ses développeurs avec un crédit de 50€ par mois. Je ne suis pas du tout familier avec AWS, je demande donc de l'aide à Kévin. Il me présente un bon nombre de services AWS. Il me conseille d'utiliser le service EC2. Cela permet d'avoir accès à un serveur en SSH où je peux installer ce dont j'ai besoin.

Après avoir démarré le service depuis la console AWS, j'installe docker et npm sur le serveur. Ce sont les deux seuls logiciels dont j'ai besoin pour l'API. Il ne me reste plus qu'à écrire mon script de déploiement. Le processus est le suivant :

  • Création de l'archive contenant le code
  • Upload sur le serveur
  • Décompression de l'archive
  • Lancement du serveur.

Voici le script :

git archive -o quixo.zip HEAD
	scp -i $(key) quixo.zip $(ssh):~/quixo-api.zip
	ssh -i $(key) $(ssh) ' \
		unzip -uo ~/quixo-api.zip -d ~/quixo-api; \
		rm -f quixo-api.zip; \
		cd ~/quixo-api; \
		NODE_ENV=production make api-install && make server-start-production; \
	'
	rm -f quixo.zip

Le déploiement de l'API s'est passé plus simplement que je ne le pensais ! La partie compliquée était la console AWS car elle permet de spécifier beaucoup d'options, ce qui augmente la complexité d'utilisation.

Connexion Front - API

J'ai désormais mon API et mon application Next.js déployés, je n'ai plus qu'à les brancher.

Mais il y a un problème : mon API est accessible en http alors que l'application déployée avec Now est en https. Lorsque le jeu est rendu côté serveur, pas de probleme ! Mais ensuite (par exemple après le déplacement d'un cube), les requêtes sont bloqués par le navigateur ! En effet, on ne peut pas faire d'appels http depuis un site en https. C'est la Same-origin policy !

Pour résoudre ce problème, ma première idée est de passer mon serveur d'API en https. Direction Let's Encrypt pour obtenir un certificat.

C'est la qu'apparait mon second problème : Let's Encrypt ne fournit pas de certificat pour les noms de domaines issus d'AWS. Une solution serait d'obtenir un nom de domaine pour mon API et d'adresser le certificat à ce nom de domaine. Mais je n'ai pas envie d'acheter un nom de domaine.

Je décide donc de sacrifier la sécurité (qui ne fait pas partie des objectifs de cette semaine) et de passer mon application Next.js en http. Now ne permet pas de faire ça, je vais donc déployer l'application de la même façon que j'ai déployé mon API : sur un service EC2 d'AWS.

L'objectif est atteint : je peux jouer au Quixo depuis internet sur mon navigateur ou sur l'application mobile avec qui je le souhaite. Comme d'habitude, le code est disponible sur GitHub

Fin de mon intégration chez Marmelab

C'était ma dernière semaine d'intégration chez Marmelab ainsi que ma dernière démo ! La semaine prochaine, je commencerai à travailler sur un projet en binôme !

Ces 5 semaines ont été pleines d'enseignements. J'ai appris la manière de travailler chez Marmelab, la rigueur attendue dans le travail, les outils utilisés, l'organisation des journées (baby-foot à 17h)...

J'ai apprécié le format de l'intégration, ça m'a permis de travailler avec presque toute l'équipe en changeant de tuteur chaque semaine. J'ai vite été mis en difficulté lors du développement. C'est d'ailleurs le but de cette période d'intégration ; chaque semaine il y a beaucoup d'objectifs à atteindre et il est interdit de travailler à la maison ! Pour ne pas perdre de temps et réussir à remplir le plus d'objectifs possibles, il fallait que je m'aide de l'expertise de mes collègues lorsque j'etais bloqué plus de quelques minutes !

Pour décrire ma période d'intégrations chez Marmelab : Accueillant, Intensif, Exigeant, Enrichissant, Original.

Je suis désormais prêt à débuter en binôme sur mon nouveau projet. Je sais que je vais retrouver tous les outils que j'ai utilisés pendant 5 semaines, le départ devrait donc être rapide !

Did you like this article? Share it!