L'essentiel en une minute

Créer une API REST Node.js en 2026, c'est d'abord choisir les bonnes versions : Node.js 24 est la LTS la plus récente au 24 octobre 2026, et Express 5.3.0 est sortie le 9 octobre. La première version de cet article reposait sur Node.js 20, arrivé en fin de vie le 30 avril 2026 : tout le code a été réécrit.

Ce tutoriel construit une petite API de gestion de tâches, de l'initialisation au déploiement. Il s'appuie sur la documentation de Node.js et d'Express, sur MDN pour HTTP et sur l'OWASP pour la sécurité, consultées le 24 octobre 2026. Le code suit ces documentations, mais nous ne l'avons pas exécuté.

Points clés

  • Visez Node.js 24 LTS aujourd'hui, ou Node.js 26 à partir du 28 octobre 2026, date à laquelle il doit passer en LTS selon le calendrier officiel. Express 5 exige Node.js 18 au minimum.
  • Express 5 transmet automatiquement les erreurs des fonctions async au gestionnaire d'erreurs : plus besoin d'envelopper chaque route dans un try/catch.
  • Le module node:sqlite, intégré à Node.js, suffit pour apprendre sans dépendance, mais il est encore au stade « release candidate » et synchrone : passez à PostgreSQL en production.
  • Pour l'authentification, l'OWASP demande de refuser alg: none, de vérifier iss, aud et exp, et de hacher les mots de passe avec Argon2id, ou à défaut scrypt.
  • Le lanceur de tests intégré à Node.js, stable depuis la version 20, et fetch suffisent pour tester une API REST Node.js sans framework de test.

Les principes REST et les codes de statut

REST n'est pas une norme mais un ensemble de contraintes d'architecture. MDN précise qu'un débutant peut voir une API REST comme un service HTTP appelable avec les outils web standard (glossaire MDN, REST, mis à jour le 11 juillet 2025). En pratique : chaque ressource a une URL (/api/v1/taches/42), le verbe HTTP dit ce qu'on en fait, le code de statut dit comment ça s'est passé.

Verbe Usage dans notre API Idempotent Code en cas de succès
GET Lire la liste ou une tâche Oui 200
POST Créer une tâche Non 201
PUT Remplacer une tâche entière Oui 200
PATCH Modifier une partie d'une tâche Non garanti 200
DELETE Supprimer une tâche Oui 204

Selon MDN, les méthodes sûres, PUT et DELETE sont idempotentes, alors que POST et PATCH ne le sont pas forcément (glossaire MDN, Idempotent) : deux PUT identiques laissent la ressource dans le même état, deux POST créent deux tâches.

Les codes de statut forment cinq classes : informations (1xx), succès (2xx), redirections (3xx), erreurs du client (4xx) et du serveur (5xx), selon la référence MDN des codes de statut. Voici ceux de notre API :

Code Sens selon MDN Quand notre API le renvoie
201 Created Une nouvelle ressource a été créée Après un POST réussi
204 No Content Pas de contenu à envoyer Après une suppression
400 Bad Request Requête incompréhensible, syntaxe invalide JSON mal formé
401 Unauthorized Le client doit s'authentifier Jeton absent, invalide ou expiré
404 Not Found Ressource introuvable Tâche inexistante ou d'un autre utilisateur
409 Conflict Conflit avec l'état du serveur E-mail déjà inscrit
413 Content Too Large Corps de requête plus grand que la limite du serveur Corps de plus de 10 ko
415 Unsupported Media Type Format de données non supporté Corps absent ou qui n'est pas du JSON
422 Unprocessable Content Requête bien formée mais erreurs sémantiques Données refusées par la validation
429 Too Many Requests Trop de requêtes en un temps donné Trop de tentatives d'inscription ou de connexion
500 Internal Server Error Situation que le serveur ne sait pas traiter Bug ou panne imprévue

La fiche REST Security de l'OWASP tranche le piège classique : 401 quand l'authentification manque ou échoue, 403 quand elle a réussi mais que l'utilisateur n'a pas les droits. Pour la tâche d'un autre utilisateur, notre API répond 404, ce qui évite de confirmer qu'elle existe.

Pourquoi une API REST Node.js avec Express en 2026

Node.js reste la technologie web la plus citée par les développeurs, et Express le framework serveur le plus utilisé de cet écosystème. Dans l'enquête Stack Overflow 2025, 48,7 % des 23 678 répondants à la question sur les technologies web déclarent avoir beaucoup travaillé avec Node.js sur l'année écoulée, et 19,9 % avec Express. NestJS est à 6,7 % et Fastify à 2,9 %.

Graphique en barres : usage de technologies serveur dans l'enquête Stack Overflow 2025, Node.js 48,7 %, Express 19,9 %, ASP.NET Core 19,7 %, FastAPI 14,8 %, Spring Boot 14,7 %, Flask 14,4 %, Django 12,6 %, NestJS 6,7 %, Fastify 2,9 %

L'édition 2026, menée du 23 juin au 5 août 2026 selon sa méthodologie, ne publie pas de classement des frameworks web : ces chiffres de 2025 restent les plus récents de cette source. Elle confirme que JavaScript est le langage le plus utilisé, par 62,0 % des 14 099 répondants à la question (Stack Overflow 2026).

Express n'est ni le plus rapide ni le plus structurant, mais sa simplicité en fait un bon outil pour apprendre : un routeur, des middlewares, rien d'imposé. Côté Python, l'équivalent serait Flask ou FastAPI, présentés dans notre guide pour apprendre Python en 2026.

Étape 1 : installer Node.js et initialiser le projet

Installez une version LTS : la page des versions de Node.js réserve la production aux versions LTS, actives ou en maintenance.

Composant Version au 24 octobre 2026 Statut
Node.js 24 « Krypton » 24.22.1, publiée le 20 octobre 2026 LTS, en maintenance depuis le 20 octobre, fin prévue le 30 avril 2028
Node.js 26 26.12.1, publiée le 21 octobre 2026 Current, passage en LTS prévu le 28 octobre 2026
Node.js 22 « Jod » 22.23.4 LTS de maintenance, fin prévue le 30 avril 2027
Node.js 20 20.20.2 Fin de vie
Express 5 5.3.0, publiée le 9 octobre 2026 Version par défaut sur npm, Node.js 18 minimum

Sources : le calendrier officiel des versions, les notes de version d'Express et sa page de support. À partir de Node.js 27, chaque version majeure deviendra LTS, sur un cycle annuel. Créez ensuite le projet :

node --version mkdir api-taches && cd api-taches npm init -y npm install express zod jsonwebtoken helmet rate-limiter-flexible 

Au 24 octobre 2026, le registre npm sert Express 5.3.0, Zod 4.6.7, jsonwebtoken 9.0.3, Helmet 8.3.0 et rate-limiter-flexible 11.2.3. Complétez package.json ainsi :

{ "name": "api-taches", "version": "1.0.0", "type": "module", "engines": { "node": ">=24" }, "scripts": { "dev": "node --watch --env-file=.env src/server.js", "start": "node --env-file-if-exists=.env src/server.js", "test": "node --test" }, "dependencies": { "express": "^5.3.0", "helmet": "^8.3.0", "jsonwebtoken": "^9.0.3", "rate-limiter-flexible": "^11.2.3", "zod": "^4.6.7" } } 

Plus besoin de nodemon ni de dotenv : selon la documentation de la ligne de commande, --watch est stable depuis Node.js 22, et --env-file comme --env-file-if-exists ne sont plus expérimentaux depuis Node.js 24.10 (journal des modifications de la 24.10.0). Le script start charge .env s'il existe, sans échouer en production où les variables sont injectées par l'hébergeur. Créez enfin un fichier .env, exclu de Git :

PORT=3000 DB_PATH=data.db JWT_SECRET=collez-ici-une-valeur-aleatoire 

Pour générer le secret, node -e "console.log(require('node:crypto').randomBytes(32).toString('hex'))" produit 256 bits aléatoires, la taille minimale qu'impose la RFC 7518, section 3.2 pour une clé HS256 ; sans cette valeur, server.js refuse de démarrer. Pour l'éditeur, voyez notre sélection d'extensions VS Code.

Étape 2 : créer le serveur Express 5

L'application et son démarrage vivent dans deux fichiers, pour que les tests importent l'application sans ouvrir de port. L'arborescence finale :

api-taches/ package.json .env src/ app.js application Express server.js démarrage db.js base SQLite schemas.js schémas Zod motdepasse.js hachage scrypt middleware/ validation, auth, limiteur, erreurs routes/ auth.js, taches.js test/ api.test.js 

Le fichier src/app.js assemble les middlewares et les routeurs :

import express from 'express'; import helmet from 'helmet'; import { routerAuth } from './routes/auth.js'; import { routerTaches } from './routes/taches.js'; import { routeInconnue, gestionErreurs } from './middleware/erreurs.js'; export const app = express(); if (process.env.TRUST_PROXY) { app.set('trust proxy', Number(process.env.TRUST_PROXY)); } app.use(helmet()); app.use(express.json({ limit: '10kb' })); app.get('/health', (req, res) => { res.json({ statut: 'ok' }); }); app.use('/api/v1/auth', routerAuth); app.use('/api/v1/taches', routerTaches); app.use(routeInconnue); app.use(gestionErreurs); 

Helmet ajoute des en-têtes de sécurité et retire X-Powered-By, comme le recommande la page sécurité en production d'Express. express.json() limite le corps à 100 ko par défaut selon la référence de l'API Express 5 : 10 ko suffisent pour une tâche. Le préfixe /api/v1 permettra une version 2 sans casser les clients. Derrière un proxy inverse, TRUST_PROXY=1 indique à Express de lire l'adresse du client dans X-Forwarded-For à un saut de distance, comme le décrit la page Express derrière un proxy ; sans proxy, laissez la variable vide, sinon un client pourrait falsifier son adresse.

Le fichier src/server.js démarre le serveur :

import { app } from './app.js'; if (Buffer.byteLength(process.env.JWT_SECRET ?? '') < 32) { throw new Error('JWT_SECRET doit contenir au moins 32 octets (256 bits)'); } const port = Number(process.env.PORT ?? 3000); app.listen(port, (error) => { if (error) throw error; console.log(`API à l'écoute sur http://localhost:${port}`); }); 

Le paramètre error est une nouveauté d'Express 5 : selon le guide de migration, app.listen appelle ce rappel en cas d'erreur du serveur, comme un port déjà occupé.

Étape 3 : stocker les données dans SQLite

Node.js embarque SQLite depuis la version 22.5, dans le module node:sqlite, sans option à activer depuis les versions 22.13 et 23.4. La documentation de Node.js 24 le classe « release candidate » (stabilité 1.2) depuis la version 24.15, publiée le 15 avril 2026 (journal des modifications de la 24.15.0). Pour un tutoriel, c'est le plus simple : zéro dépendance, zéro serveur.

import { DatabaseSync } from 'node:sqlite'; export const db = new DatabaseSync(process.env.DB_PATH ?? 'data.db'); db.exec(` CREATE TABLE IF NOT EXISTS utilisateurs ( id INTEGER PRIMARY KEY, email TEXT NOT NULL UNIQUE, hash TEXT NOT NULL ); CREATE TABLE IF NOT EXISTS taches ( id INTEGER PRIMARY KEY, titre TEXT NOT NULL, terminee INTEGER NOT NULL DEFAULT 0, proprietaire INTEGER NOT NULL REFERENCES utilisateurs(id) ); `); 

Les clés étrangères sont actives par défaut, selon la même page. Toutes les requêtes passent par db.prepare() avec des paramètres ? : les valeurs sont liées à part du texte SQL, ce qui bloque l'injection SQL.

Deux limites. Sous Node.js 26, la classe s'appelle Database et DatabaseSync devient un alias déprécié dans la documentation seulement (DEP0210). Surtout, l'API est synchrone : chaque requête bloque la boucle d'événements, alors que la page performances en production d'Express déconseille les fonctions synchrones.

Graphique en sucettes : bases de données candidates pour une API REST Node.js, les plus utilisées selon l'enquête Stack Overflow 2026, PostgreSQL 57,9 %, SQLite 42,1 %, MySQL 35,1 %, Redis 28,3 %, Microsoft SQL Server 27,1 %, MariaDB 20,3 %, MongoDB 19,0 %

Pour une API REST Node.js en production avec du trafic concurrent, préférez PostgreSQL, la base la plus utilisée : 57,9 % des 12 061 répondants concernés de l'enquête Stack Overflow 2026, devant SQLite (42,1 %) et MySQL (35,1 %) ; MongoDB est à 19,0 %. Il faudra un pilote asynchrone et des await, mais les routes gardent la même forme.

Étape 4 : écrire les routes et les verbes HTTP

Les routes traduisent le tableau des verbes en code. Toutes passent par exigerAuth : chaque utilisateur ne voit que ses tâches. Les middlewares utilisés sont écrits aux étapes 5 et 7. Voici src/routes/taches.js :

import { Router } from 'express'; import { db } from '../db.js'; import { exigerAuth } from '../middleware/auth.js'; import { exigerJson, valider } from '../middleware/validation.js'; import { schemaCreation, schemaRemplacement, schemaModification } from '../schemas.js'; export const routerTaches = Router(); routerTaches.use(exigerAuth); const versJson = (t) => ({ id: t.id, titre: t.titre, terminee: t.terminee === 1 }); const introuvable = (res) => res.status(404).json({ erreur: 'Tâche introuvable' }); function lireId(req) { const id = Number(req.params.id); return Number.isSafeInteger(id) && id > 0 ? id : null; } function trouver(id, proprietaire) { return db .prepare('SELECT id, titre, terminee FROM taches WHERE id = ? AND proprietaire = ?') .get(id, proprietaire); } routerTaches.get('/', (req, res) => { const taches = db .prepare('SELECT id, titre, terminee FROM taches WHERE proprietaire = ? ORDER BY id') .all(req.utilisateurId); res.json(taches.map(versJson)); }); routerTaches.get('/:id', (req, res) => { const id = lireId(req); const tache = id && trouver(id, req.utilisateurId); if (!tache) return introuvable(res); res.json(versJson(tache)); }); routerTaches.post('/', exigerJson, valider(schemaCreation), (req, res) => { const { titre, terminee = false } = req.donnees; const { lastInsertRowid } = db .prepare('INSERT INTO taches (titre, terminee, proprietaire) VALUES (?, ?, ?)') .run(titre, terminee ? 1 : 0, req.utilisateurId); const id = Number(lastInsertRowid); res.status(201).location(`${req.baseUrl}/${id}`).json({ id, titre, terminee }); }); function mettreAJour(req, res) { const id = lireId(req); const actuelle = id && trouver(id, req.utilisateurId); if (!actuelle) return introuvable(res); const tache = { ...versJson(actuelle), ...req.donnees }; db.prepare('UPDATE taches SET titre = ?, terminee = ? WHERE id = ?') .run(tache.titre, tache.terminee ? 1 : 0, id); res.json(tache); } routerTaches.put('/:id', exigerJson, valider(schemaRemplacement), mettreAJour); routerTaches.patch('/:id', exigerJson, valider(schemaModification), mettreAJour); routerTaches.delete('/:id', (req, res) => { const id = lireId(req); if (!id) return introuvable(res); const { changes } = db .prepare('DELETE FROM taches WHERE id = ? AND proprietaire = ?') .run(id, req.utilisateurId); if (Number(changes) === 0) return introuvable(res); res.sendStatus(204); }); 

Le guide de migration vers Express 5 retire res.json(objet, 201) au profit de res.status(201).json(objet), et res.send(204) au profit de res.sendStatus(204). L'en-tête Location du POST donne au client l'URL de la ressource créée.

La syntaxe des chemins a aussi changé : un joker doit être nommé (/*splat au lieu de /*) et un paramètre optionnel s'écrit entre accolades. Pour migrer un projet Express 4, le guide propose npx codemod@latest @expressjs/v5-migration-recipe.

Étape 5 : valider les données avec Zod

Ne faites jamais confiance au corps d'une requête. La fiche REST de l'OWASP demande de valider longueur, plage, format et type de chaque entrée, et de refuser en 415 un Content-Type inattendu. Les schémas vont dans src/schemas.js :

import * as z from 'zod'; const titre = z.string().trim().min(1).max(200); export const schemaIdentifiants = z.strictObject({ email: z.string().trim().toLowerCase().pipe(z.email()), motDePasse: z.string().min(15).max(128), }); export const schemaCreation = z.strictObject({ titre, terminee: z.boolean().optional() }); export const schemaRemplacement = z.strictObject({ titre, terminee: z.boolean() }); export const schemaModification = schemaRemplacement.partial(); 

z.strictObject refuse les clés inconnues : impossible d'injecter un champ proprietaire. L'e-mail est nettoyé et mis en minuscules avant d'être validé par z.email(), la syntaxe de Zod 4 (référence de l'API Zod), pour qu'Ada@Example.com et ada@example.com désignent le même compte. Le minimum de 15 caractères suit la fiche Authentication de l'OWASP et la norme NIST SP 800-63B, révision 4 (section 3.1.1.2) pour un mot de passe utilisé sans second facteur ; le maximum doit rester d'au moins 64 caractères. Le middleware src/middleware/validation.js range le résultat nettoyé dans req.donnees :

import * as z from 'zod'; export function exigerJson(req, res, next) { if (!req.is('application/json')) { return res.status(415).json({ erreur: 'Le corps doit être en application/json' }); } next(); } export function valider(schema) { return (req, res...
Source:https://dev.to/neurau_tech/creer-une-api-rest-nodejs-avec-express-en-2026-le-tutoriel-pas-a-pas-3c7i