Accueil / Articles / Un cadre pratique pour mettre les applications Node.js en production

Un cadre pratique pour mettre les applications Node.js en production

Découvrez une liste de contrôle complète pour la production des applications Node.js, couvrant les serveurs, les secrets, la gestion des processus, HTTPS, CI/CD, le journalisation et les sauvegardes.

2042 mots

Faire tourner une application Node.js sur sa propre machine, c’est la partie facile.

npm run dev

Votre API répond. Votre base de données se connecte. Tout semble fonctionner correctement.

Puis quelqu’un demande :

"D’accord, comment déployons-nous cela en production ?"

C’est là que l’on se rend compte que la création de l’API n’était qu’une moitié du travail.

Passer en production soulève toute une série de nouvelles questions :

  • Où l’application va-t-elle vraiment tourner ?
  • Comment la faire continuer à fonctionner en cas de panne ?
  • Comment le trafic entrant atteint-il le processus Node.js ?
  • Où devraient être stockées les variables d’environnement ?
  • Comment configurer HTTPS ?
  • Comment déployer de nouvelles versions ?
  • Comment détecter les erreurs lorsqu’elles se produisent ?
  • Que se passe-t-il si le serveur redémarre lui-même ?

Voici un cadre pratique pour réfléchir à une configuration de production Node.js.

1. Comprendre l’architecture de production

                    Internet
                       │
                       ▼
                  ┌─────────┐
                  │  Nginx  │
                  │  :80/443│
                  └────┬────┘
                       │
                       ▼
                ┌─────────────┐
                │   Node.js   │
                │ Application │
                └──────┬──────┘
                       │
              ┌────────┼────────┐
              ▼        ▼        ▼
          PostgreSQL  Redis   External APIs

Le principe fondamental est que les utilisateurs finaux ne devraient généralement pas se connecter directement à quelque chose comme :

localhost:3000

À la place, Nginx se trouve en amont, acceptant les requêtes publiques et les transmettant à votre application Node.js.

2. Préparer le serveur

Vous pouvez mettre en place un VPS ou une machine virtuelle dans le cloud auprès d’un fournisseur tel que AWS, GCP, Azure ou DigitalOcean.

Avant toute autre chose, vous devez configurer la machine elle-même.

Sous Ubuntu, cela commence généralement par :

sudo apt update
sudo apt upgrade -y

Ensuite, installez tous les outils dont votre application a besoin.

Pour Node.js lui-même, il est généralement conseillé de l’installer à l’aide d’un gestionnaire de versions comme nvm, afin de pouvoir contrôler précisément quelle version s’exécute.

Vérifiez les versions installées avec :

node -v
npm -v

La version de Node qui s’exécute en production doit correspondre à celle que vous testez localement.

Cela peut sembler un détail mineur, mais des versions incompatibles peuvent provoquer des bugs frustrants et difficiles à diagnostiquer une fois le déploiement effectué.

3. Ne mettez pas de secrets dans votre code

Cette erreur est facile à commettre et facile à éviter.

Évitez d’insérer des identifiants de manière codée, comme ceci :

const DATABASE_URL =
  "postgresql://user:password@database.com/mydb";

Et n’ajoutez jamais de secrets dans l’historique de Git.

Préférez les définir comme variables d’environnement :

NODE_ENV=production
PORT=3000
DATABASE_URL=postgresql://...
REDIS_URL=redis://...
JWT_SECRET=...

Puis lisez-les dans votre code en utilisant :

process.env.DATABASE_URL

Assurez-vous que vos fichiers .env soient exclus du contrôle de version :

.env
.env.production

Un seul mot de passe de base de données exposé peut causer bien plus de dommages que n’importe quelle erreur de déploiement.

4. Construire l’application

npm ci
npm run build

Puis lancez le résultat compilé avec :

npm start

Les commandes exactes varieront en fonction de votre stack technique.

Ce qui compte, c’est ce principe :

Le trafic de production doit atteindre la version compilée pour production, jamais votre serveur de développement.

npm run dev

en tant que processus actif.

5. Que se passe-t-il si Node.js plante ?

Supposons que vous lanciez votre application directement de cette manière :

node dist/server.js

À un moment donné, quelque chose ne fonctionne pas et le processus s’arrête de manière inattendue.

Votre API est désormais hors ligne, sans aucun moyen de la remettre en service.

C’est précisément le problème que résout un gestionnaire de processus.

PM2 est une solution largement utilisée à cette fin.

Installez-le de manière globale :

npm install -g pm2

Puis lancez votre application sous la supervision de PM2 :

pm2 start dist/server.js --name my-api

Vérifiez son état :

pm2 status

Consultez les journaux :

pm2 logs my-api

Ou redémarrez-le sur demande :

pm2 restart my-api

Il ne s’agit pas seulement de facilité d’utilisation. Il s’agit plutôt du fait que votre processus est désormais activement supervisé, au lieu de tourner dans une fenêtre de terminal en espérant que rien ne le mette à l’arrêt.

6. Faire en sorte que l’application démarre après un redémarrage du serveur

Les serveurs sont effectivement redémarrés, que ce soit intentionnellement ou non.

Par exemple :

Server reboot
     ↓
Operating system starts
     ↓
Node.js application?

Vous ne voulez pas devoir vous connecter manuellement par SSH à chaque fois que cela se produit.

PM2 peut générer un script de démarrage pour votre système :

pm2 startup

Ensuite, conservez la liste des processus en cours d’exécution :

pm2 save

Avec cela en place, votre application peut se reconnecter automatiquement après un redémarrage.

7. Placer Nginx devant Node.js

Disons que votre serveur Node.js est configuré pour écouter sur :

localhost:3000

Mais vos utilisateurs se connectent depuis :

https://api.example.com

Nginx peut combler cette lacune en agissant comme un proxy inversé.

Une configuration simplifiée pourrait ressembler à ceci :

server {
    listen 80;server_name api.example.com;
    location / {
        proxy_pass http://localhost:3000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

Avec cela, le chemin de la requête devient :

User
  ↓
https://api.example.com
  ↓
Nginx :80
  ↓
Node.js :3000

De cette manière, le port sur lequel votre application Node.js écoute n’a jamais besoin d’être exposé directement à Internet.

8. Ajouter HTTPS

Faire fonctionner votre API de production en mode simple

http://

Cela n’est pas suffisant. Il faut que cela soit fourni via

https://

Une approche largement utilisée consiste à combiner Let’s Encrypt avec Certbot.

Commencez par installer Certbot :

sudo apt install certbot python3-certbot-nginx

Puis demandez et appliquez un certificat pour votre domaine :

sudo certbot --nginx -d api.example.com

Certbot s’occupe de fournir le certificat et d’activer HTTPS à votre place.

Lorsque c’est fait, le flux de demande ressemble à ceci :

Client
   ↓
HTTPS
   ↓
Nginx
   ↓
Node.js
   ↓
Database / Redis

9. N’oubliez pas votre pare-feu

Tous les ports de votre serveur n’ont pas besoin d’être accessibles depuis l’extérieur.

Généralement, vous souhaitez que le public puisse accéder à :

80   → HTTP
443  → HTTPS

ainsi qu’à l’accès SSH sur :

22

Les ports utilisés par PostgreSQL ou Redis, en revanche, ne devraient généralement pas être ouverts à Internet sauf si vous avez une raison spécifique et des contrôles d’accès solides en place.

Les règles exactes varieront en fonction de votre configuration, mais le principe directeur reste le même :

Ne mettez en exposition que ce qui doit réellement l’être.

10. Le déploiement manuel fonctionne, jusqu’au jour où il ne le fait plus

Débutant, un déploiement peut simplement consister en :

git pull
npm install
npm run build
pm2 restart my-api

C’est tout à fait acceptable pour un petit projet.

Mais tôt ou tard, vous remarquerez que vous reproduisez constamment la même séquence :

Developer pushes code
       ↓
SSH into server
       ↓
git pull
       ↓
install dependencies
       ↓
build
       ↓
restart

À ce stade, il est utile de l’automatiser.

11. CI/CD modifie le flux de travail

Avec des outils comme GitHub Actions, le processus peut passer à :

Developer
    ↓
git push
    ↓
GitHub
    ↓
CI/CD Pipeline
    ↓
Build + Test
    ↓
Deploy
    ↓
Production

Une définition minimale de flux de travail pourrait ressembler à :

name: Deploy
on:
  push:
    branches:
      - mainjobs:
  deploy:
    runs-on: ubuntu-latest    steps:
      - uses: actions/checkout@v4      - name: Install dependencies
        run: npm ci      - name: Build
        run: npm run build      - name: Test
        run: npm test

Les étapes spécifiques de déploiement dépendront de votre infrastructure propre.

Ce qui importe davantage, c’est le principe fondamental :

Ne mettez pas en automatique un processus de déploiement que vous ne comprenez pas encore.

Apprenez d’abord à le faire manuellement.

Seulement ensuite, transformez les étapes répétitives en automatisation.

12. Le journalisation n’est pas optionnelle

C’est grâce aux journaux que vous le découvrirez.

Au minimum, vous souhaitez des réponses aux questions suivantes :

When did the error happen?
Which endpoint failed?
What status code was returned?
What was the error?
How long did the request take?

Un exemple de base :

console.error({
  message: error.message,
  endpoint: req.originalUrl,
  method: req.method,
  timestamp: new Date().toISOString()
});

Pour tout système fonctionnant à grande échelle, des journaux structurés associés à une collecte centralisée des logs vous seront bien plus utiles que l’utilisation dispersée de fonctions console.log() dans votre code.

13. Surveillez plus que les erreurs

L’absence d’exceptions ne signifie pas que votre système est en bon état.

Vous souhaitez également disposer d’informations sur des éléments tels que :

CPU
Memory
Disk
Request latency
Error rate
Database performance
Redis health
Traffic

Par exemple :

Requests       → 1,500/min
Average latency → 180ms
Error rate      → 0.4%
CPU             → 42%
Memory          → 61%

De telles métriques vous fournissent une image bien plus complète de la performance réelle de votre système.

14. Les sauvegardes sont plus importantes que le déploiement

Voici une situation à laquelle la plupart des développeurs préféreraient ne pas penser :

Production database
       ↓
Something goes wrong
       ↓
Data disappears

Vous pouvez toujours redéployer le code de votre application.

Cependant, votre base de données peut contenir des données qui ne peuvent tout simplement pas être régénérées une fois perdues.

C’est pourquoi les sauvegardes ne sont pas non plus optionnelles.

Vous avez besoin d’un plan de secours pour tout ce qui est important en production, et tout aussi important, vous devez savoir comment restaurer à partir de ces sauvegardes.

15. Les déploiements sans interruption sont un problème distinct

À un certain moment, mettre temporairement votre application hors ligne pour effectuer un déploiement cesse d’être acceptable.

Considérez ce scénario :

Old version running
       ↓
New version deployed
       ↓
Traffic gradually moves
       ↓
Old version removed

En fonction de la configuration de votre infrastructure, vous pourriez recourir à :

  • Le lancement de plusieurs copies de votre processus Node.js en parallèle
  • Le mode cluster intégré de PM2
  • Un équilibreur de charge qui distribue le trafic entre les instances
  • Le déploiement progressif des mises à jour, nœud par nœud, plutôt que d’un coup
  • Le transfert du trafic entre un environnement ancien et un nouveau (mode bleu-vert)
  • L’emballage de l’application dans des conteneurs
  • L’orchestration de tout cela avec Kubernetes

Cependant, disposer d’une API Node.js ne signifie pas automatiquement que vous avez besoin de Kubernetes.

Méfiez-vous des solutions trop complexes au début ; développez l’architecture uniquement lorsque vos besoins réels l’exigent.

16. Ma liste de contrôle pour la production

Au préalable de considérer qu’une application Node.js est prête pour la production, voici ce qu’il convient de vérifier :

[ ] Production environment configured
[ ] Secrets stored securely
[ ] Database connection configured
[ ] Redis configured if required
[ ] Production build tested
[ ] Process manager configured
[ ] Application restart tested
[ ] Nginx configured
[ ] HTTPS configured
[ ] Firewall configured
[ ] Logs available
[ ] Error monitoring configured
[ ] Database backups configured
[ ] Backup restoration tested
[ ] Deployment process documented
[ ] CI/CD configured if needed
[ ] Health check endpoint available

17. L’architecture que j’ai en tête

Une configuration de base pour un système Node.js en environnement de production tend généralement à ressembler à ceci :

                    ┌─────────────┐
                    │   Internet  │
                    └──────┬──────┘
                           │
                           ▼
                    ┌─────────────┐
                    │    Nginx    │
                    │  SSL / Proxy│
                    └──────┬──────┘
                           │
                 ┌─────────┴─────────┐
                 ▼                   ▼
          ┌────────────┐      ┌────────────┐
          │  Node.js   │      │  Node.js   │
          │ Instance 1 │      │ Instance 2 │
          └─────┬──────┘      └─────┬──────┘
                │                   │
                └─────────┬─────────┘
                          │
              ┌───────────┼───────────┐
              ▼           ▼           ▼
         PostgreSQL     Redis    External APIs

Et autour de ce noyau, vous voudrez généralement :

Monitoring
Logging
Backups
CI/CD
Security

Pensée finale

Déployer une application Node.js en production n’est jamais simplement :

npm start

Le travail réel en environnement de production implique de planifier tout ce qui peut se produire après le démarrage du processus.

Quel est votre plan en cas de panne inattendue ?

Comment le système se comporte-t-il une fois que le serveur redémarre ?

Quelle est la réponse en cas d’augmentation soudaine du trafic entrant ?

Que se passe-t-il dès que la base de données devient inaccessible ?

Comment récupérer les choses après le déploiement d’une version défectueuse ?

Quel est le protocole à suivre si des identifiants sont exposés par erreur ?

C’est là la différence entre :

« Ça fonctionne sur mon ordinateur. »

et

« Ça fonctionne de manière fiable en production. »

Vous n’avez pas besoin d’une infrastructure complexe dès le premier jour.

Démarrez petit.

Comprenez chaque élément que vous ajoutez.

Automatisez tout ce qui se répète.

Surveillez les métriques qui sont vraiment importantes.

N’introduisez de la complexité que lorsque le système l’exige réellement.

Le déploiement n’est pas la ligne d’arrivée du développement. C’est là que le fonctionnement réel de votre logiciel commence.

Lectures complémentaires

  • Construire un gestionnaire d’erreurs de niveau production dans des apps Node.js — Apprenez à classifier les erreurs Node.js, à concevoir une hiérarchie d’erreurs personnalisée, à centraliser la gestion des erreurs asynchrones et à protéger les traces d’exécution afin d’assurer la résilience en production.