Créer un serveur API avec NestJS : le guide ultime
Quand j'ai commencé à développer des serveurs API en NodeJS, j'avais beaucoup de mal à utiliser Express. J'étais débutant, et je ne connaissais pas encore tous les concepts de développeur : réutiliser du code, fonctions asynchrones et Typescript.
Je voulais voir comment les pros faisaient. Je voulais lire du code propre et bien organisé. Je ne voulais surtout pas coder toutes mes routes dans le même fichier !
Puis j'ai découvert NestJS. Son générateur de projet m'a été d'une grande aide. Je pouvais, grâce à son outil CLI générer une base de code en Typescript avec Express déjà configuré. Il y avait des exemples de routes, un contrôleur, un service et un module.
En plus, la documentation était très concise. Ça m'a plu.
Pourquoi utiliser NestJS comme serveur API ?
J'avais un besoin spécifique : Créer une base de données pour sauvegarder des données. Récupérer ces données côté client et les afficher. Et pouvoir les modifier (toujours côté client).
NestJS a répondu à ce besoin rapidement. Donc je l'ai choisi.
Je recommande ce framework aux développeurs qui veulent créer un serveur API en Javascript. Mais je ne le recommande pas parce qu'il est mieux que les autres (il l'est 😉).
Je le recommande, car il est dogmatique : il impose une structure de code. Il nous force à réfléchir à la manière dont on va organiser notre code. Pour un développeur débutant, c'est un excellent moyen d'apprendre à coder proprement.
Pré-requis
Assure-toi d'avoir Node et NPM installés sur votre machine. Des connaissances en Javascript et Git sont recommandées. Nous allons aussi beaucoup utiliser le terminal de commande.
Ce que tu vas apprendre et construire
- Les bases de NestJS (routes, contrôleurs, services, modules)
- Utiliser l'ORM Prisma pour gérer la base de données
- Créer quelques comptes utilisateurs (connexion et inscription par email / mot de passe)
- Envoyer des alertes par email (nouveau compte, mot de passe oublié) avec Courier
- Ajouter l'authentification avec JWT pour les identifier
- FRONTEND : Instantier un projet React avec Remix
- Créer un formulaire d'inscription et de connexion
- Connecter le frontend et le backend
- Créer un chat en temps réel avec Socket.io (implémentation backend)
- Implémenter le chat côté frontend
- Héberger le code source sur Github (backend et frontend)
- DÉPLOIEMENT : Commander un serveur d'hébergement (VPS) sur Amazon Lightsail
- Configurer le nom de domaine avec Amazon Route 53
- Configurer Caddy sur le serveur
- Créer le fichier Docker pour le serveur (et le frontend)
- Configurer les Github Actions pour héberger notre code source à chaque commit
- Ajouter les variables d'environnement au repository Github
- Tester l'application en production
- FEATURE : Ajouter la logique d'upload des images
- Héberger les images sur AWS S3
- FEATURE : Ajouter la logique de paiement avec Stripe
- Paramétrer et écouter les websockets de Stripe pour recevoir les évènements de paiement
Stack technique
Voici la stack que j'utilise au quotidien :
Côté serveur (backend)
- Framework NestJS
- librairie Fastify comme serveur HTTP. Pareil qu'Express, mais 2x plus rapide.
- Prisma ORM pour définir le modèle de nos données
- base de donnée MySQL hébergée sur Planetscale
- Socket.io pour envoyer les messages en temps réel (websockets)
- Stripe pour envoyer de l'argent
- Courier / Resend pour envoyer les emails (compte créé, mot de passe oublié, nouveau message reçu ...)
- AWS S3 pour héberger les images
- Passport pour gérer l'authentification en créant un token sécurisé JWT
- Zod pour valider les données
- et bien d'autres ...
Côté client (frontend)
- React 18 pour créer une belle UI
- Framework Remix pour gérer nos routes et le rendu côté serveur (SSR)
- Tailwind CSS pour le design du chat
- Également Zod, Stripe et Socket.io
Déploiement
- Amazon Route 53 pour le nom de domaine
- Amazon Lightsail pour le serveur d'hébergement (VPS)
- Github Actions pour déployer automatiquement notre code
- Docker pour exécuter notre code dans un environnement virtualisé
- Caddy pour gérer les certificats SSL
Formation vidéo
Tu peux également suivre ce guide au format vidéo.
Créer ton premier serveur API avec NestJS (Authentification par JWT)
Développe ta MESSAGERIE en TEMPS RÉEL avec Socket.io
Héberge tes IMAGES sur AWS S3 en Javascript
Gère des PAIEMENTS en JAVASCRIPT avec STRIPE.JS
Installation du projet
Maintenant c'est à toi de jouer !
Pour créer ta super API, tu dois d'abord installer l'outil CLI.
Tu vas maintenant créer le dossier du projet. Il sera généré dans le dossier où tu exécutes la commande terminal. (Tu peux entrer pwd
pour voir le chemin de ton dossier actuel)
Architecture du projet
Avec NestJS, la bonne pratique est de nommer les fichiers en fonction de leur type.
Voici les 4 principaux types de fichiers :
Le service
Toute la logique de notre application se trouve dans les services. Modification des données, envoi d'emails, authentification ... Ces fichiers contiennent le plus de lignes de code. Si tu fais un CRUD, tu écriras toute la logique dans un service.
Voici le service de base :
Notez le décorateur @Injectable()
. NestJS utilise le concept d'Injection de Dépendances. Ce décorateur signifie que ce service peut être injecté (et utilisé) dans un autre service.
Pour utiliser la méthode getHello()
, tu vas devoir injecter le service AppService à l'intérieur d'un autre service. Tu ne pourras pas "importer" directement la méthode.
Je t'en parle plus en détail juste en dessous.
Le contrôleur
Les contrôleur permettent de définir les routes. Une route est accessible via une URL.
Voici un exemple de contrôleur (j'ai rajouté une route /posts
) qui est accessible via l'URL http://localhost:3000/posts
Après la déclaration de l'AppController, tu vois une fonction constructor
. Elle permet d'injecter le service AppService dans le contrôleur. C'est l'Injection de Dépendance. Pour utiliser AppService, tu l'injectes dans le constructeur. Si tu utilises TypeScript, la syntaxe paraît familière.
La méthode constructor()
sera exécutée à l'initialisation du fichier. Dans cet exemple, elle contient un paramètre private readonly appService: AppService
.
Le mot-clé private
rend la variable inaccessible de l'extérieur (les fichiers important AppController n'y auront pas accès).
Le mot-clé readonly
rend la variable non modifiable. (Au cas où tu modifies la valeur de appService
par erreur).
Tu accèdes aux méthodes public de AppService via this.appService
.
Le Module
Le module est le fichier le plus important. Il permet de définir les contrôleurs, les services et potentiellement d'autres modules.
Voici un exemple de module :
Pour que la route déclarée dans AppController soit accessible, tu as besoin d'importer ton fichier contrôleur dans le module.
Le décorateur @Module
permet de définir les contrôleurs, les services et les modules importés.
Par exemple, notre module d'Authentification aura besoin du module JWT pour générer des tokens sécurisés.