Créer une API REST avec Node.js

18 mars 2026 8 min

Introduction : Qu’est-ce qu’une API REST ?

Une API REST (Representational State Transfer) est un ensemble de règles qui permet à des applications de communiquer entre elles via HTTP. C’est le standard de facto pour créer des services web modernes. Concrètement, une API REST expose des endpoints (URLs) que d’autres logiciels peuvent appeler pour obtenir ou envoyer des données.

Anecdote perso : La première fois que j’ai créé une API REST, c’était pour un projet de gestion de stock pendant mes études. J’ai passé 2 jours à comprendre pourquoi mes requêtes POST ne marchaient pas. Le problème ? J’avais oublié le middleware express.json(). Résultat : req.body était toujours undefined. Depuis, je le mets TOUJOURS en premier.

Exemples d’usage :

  • Une application mobile qui récupère la liste des utilisateurs depuis un serveur
  • Un site e-commerce qui gère son catalogue de produits via des requêtes HTTP
  • Un dashboard qui affiche des statistiques en temps réel

Dans ce tutoriel, nous allons créer une API REST complète avec Node.js et Express.js, capable de gérer une liste de tâches (todo list). Tu apprendras les verbes HTTP (GET, POST, PUT, DELETE) et comment structurer ton API.

Prérequis

Avant de commencer, assure-toi d’avoir :

  • Node.js installé (version 18+) : vérifie avec node --version
  • npm (installé automatiquement avec Node.js)
  • Un éditeur de texte (VS Code, Sublime Text, etc.)
  • Notions de base en JavaScript (variables, fonctions, objets)
  • Un outil de test d’API : Postman, Insomnia, ou simplement curl en ligne de commande

Si Node.js n’est pas installé, rends-toi sur nodejs.org et télécharge la version LTS.

Mon conseil tranché : Utilise curl pour tester tes endpoints. Postman, c’est bien, mais curl te force à comprendre vraiment ce que tu envoies. Et puis, sur un serveur Linux, tu n’auras pas Postman. Autant prendre les bonnes habitudes maintenant.

Étape 1 : Initialiser le projet

Crée un nouveau dossier pour ton projet et initialise un projet Node.js :

mkdir todo-api
cd todo-api
npm init -y

La commande npm init -y génère un fichier package.json avec les paramètres par défaut. Ce fichier contient les métadonnées de ton projet et la liste des dépendances.

Étape 2 : Installer Express.js

Express.js est un framework web minimaliste pour Node.js qui simplifie la création de serveurs HTTP. Installe-le avec npm :

npm install express

Cela télécharge Express et l’ajoute dans le dossier node_modules. Ton package.json est automatiquement mis à jour avec cette dépendance.

Étape 3 : Créer le serveur de base

Crée un fichier server.js à la racine de ton projet et ajoute ce code :

const express = require('express');
const app = express();
const PORT = 3000;

// Middleware pour parser le JSON dans les requêtes
app.use(express.json());

// Route de test
app.get('/', (req, res) => {
  res.json({ message: 'Bienvenue sur l\'API Todo !' });
});

// Démarrage du serveur
app.listen(PORT, () => {
  console.log(`Serveur démarré sur http://localhost:${PORT}`);
});

Explication ligne par ligne :

  • const express = require('express'); : importe Express
  • const app = express(); : crée une instance d’application Express
  • app.use(express.json()); : middleware qui permet de recevoir des données JSON dans les requêtes. NE L’OUBLIE PAS.
  • app.get('/', ...) : définit une route GET sur /
  • res.json(...) : envoie une réponse JSON au client
  • app.listen(PORT, ...) : démarre le serveur sur le port 3000

Lancer le serveur :

node server.js

Ouvre ton navigateur à http://localhost:3000. Tu devrais voir :

{"message":"Bienvenue sur l'API Todo !"}

✅ Ton serveur fonctionne !

Erreur classique que j’ai vécue : J’ai lancé node server.js et rien ne s’affichait. En fait, j’avais un autre serveur qui tournait déjà sur le port 3000. Résultat : conflit de port. Maintenant, je vérifie toujours avec lsof -i :3000 avant de lancer un nouveau serveur.

Étape 4 : Créer le modèle de données (en mémoire)

Pour simplifier, nous allons stocker les tâches dans un tableau en mémoire. Plus tard, tu pourras remplacer cela par une vraie base de données (MongoDB, PostgreSQL, etc.).

Ajoute ce code après const PORT = 3000; :

// Données en mémoire
let todos = [
  { id: 1, title: 'Apprendre Node.js', completed: false },
  { id: 2, title: 'Créer une API REST', completed: false },
  { id: 3, title: 'Déployer sur un serveur', completed: false }
];

let nextId = 4;

Opinion tranchée : Les données en mémoire, c’est parfait pour apprendre. Mais JAMAIS en production. Dès que tu redémarres le serveur, tout est perdu. Passe à une vraie DB dès que ton concept fonctionne.

Étape 5 : Créer les routes CRUD

GET /todos – Lister toutes les tâches

app.get('/todos', (req, res) => {
  res.json(todos);
});

Teste avec curl :

curl http://localhost:3000/todos

GET /todos/:id – Récupérer une tâche par ID

app.get('/todos/:id', (req, res) => {
  const id = parseInt(req.params.id);
  const todo = todos.find(t => t.id === id);
  
  if (!todo) {
    return res.status(404).json({ error: 'Tâche introuvable' });
  }
  
  res.json(todo);
});

Teste :

curl http://localhost:3000/todos/1

Piège classique : Oublier le parseInt(). req.params.id est une chaîne de caractères, pas un nombre. Si tu compares "1" === 1, ça retourne false. Ça m’arrive encore parfois.

POST /todos – Créer une nouvelle tâche

app.post('/todos', (req, res) => {
  const { title } = req.body;
  
  if (!title) {
    return res.status(400).json({ error: 'Le champ title est requis' });
  }
  
  const newTodo = {
    id: nextId++,
    title,
    completed: false
  };
  
  todos.push(newTodo);
  res.status(201).json(newTodo);
});

Teste avec curl :

curl -X POST http://localhost:3000/todos \
  -H "Content-Type: application/json" \
  -d '{"title":"Lire la doc Express"}'

Mon expérience : J’ai oublié le header Content-Type: application/json au moins 50 fois. Sans ce header, Express ne parse pas le body. Résultat : req.body est vide. Maintenant, je le mets par réflexe.

PUT /todos/:id – Mettre à jour une tâche

app.put('/todos/:id', (req, res) => {
  const id = parseInt(req.params.id);
  const todo = todos.find(t => t.id === id);
  
  if (!todo) {
    return res.status(404).json({ error: 'Tâche introuvable' });
  }
  
  const { title, completed } = req.body;
  if (title !== undefined) todo.title = title;
  if (completed !== undefined) todo.completed = completed;
  
  res.json(todo);
});

Teste :

curl -X PUT http://localhost:3000/todos/1 \
  -H "Content-Type: application/json" \
  -d '{"completed":true}'

DELETE /todos/:id – Supprimer une tâche

app.delete('/todos/:id', (req, res) => {
  const id = parseInt(req.params.id);
  const index = todos.findIndex(t => t.id === id);
  
  if (index === -1) {
    return res.status(404).json({ error: 'Tâche introuvable' });
  }
  
  todos.splice(index, 1);
  res.status(204).send();
});

Teste :

curl -X DELETE http://localhost:3000/todos/1

Erreur classique : Utiliser find() au lieu de findIndex() pour supprimer. find() retourne l’élément, pas son indice. Du coup, splice() ne marche pas. Ça m’arrive encore.

Étape 6 : Gestion des erreurs

Ajoute un middleware de gestion d’erreurs à la fin (avant app.listen) :

// Gestion des routes inexistantes
app.use((req, res) => {
  res.status(404).json({ error: 'Route introuvable' });
});

// Gestion des erreurs globales
app.use((err, req, res, next) => {
  console.error(err.stack);
  res.status(500).json({ error: 'Erreur serveur' });
});

Étape 7 : Améliorer avec nodemon (optionnel)

Pendant le développement, relancer manuellement le serveur à chaque modification est pénible. nodemon redémarre automatiquement ton serveur :

npm install --save-dev nodemon

Ajoute dans package.json :

"scripts": {
  "start": "node server.js",
  "dev": "nodemon server.js"
}

Lance avec :

npm run dev

Mon conseil : nodemon, c’est un gain de temps énorme. Installe-le dès le début. Tu me remercieras plus tard.

Erreurs courantes

❌ Cannot GET /todos

Cause : Route mal définie ou serveur non démarré.

Solution : Vérifie que app.get('/todos', ...) est bien présent et que le serveur tourne.

❌ req.body undefined

Cause : Middleware express.json() manquant.

Solution : Ajoute app.use(express.json()); avant tes routes.

Mon expérience : J’ai passé 1h à debugger ça la première fois. Maintenant, je vérifie TOUJOURS que ce middleware est présent avant de tester une requête POST.

❌ Port 3000 already in use

Cause : Un autre processus utilise déjà le port 3000.

Solution : Change const PORT = 3000; en 3001 ou tue le processus existant avec lsof -i :3000 puis kill -9 PID.

Résultat attendu

À la fin de ce tutoriel, tu disposes d’une API REST fonctionnelle avec :

  • ✅ GET /todos : liste des tâches
  • ✅ GET /todos/:id : détail d’une tâche
  • ✅ POST /todos : création d’une tâche
  • ✅ PUT /todos/:id : mise à jour d’une tâche
  • ✅ DELETE /todos/:id : suppression d’une tâche
  • ✅ Gestion d’erreurs basique

Prochaines étapes

Pour aller plus loin :

  1. Connecter une base de données : MongoDB avec Mongoose, ou PostgreSQL avec Sequelize
  2. Ajouter de l’authentification : JWT (JSON Web Tokens) pour sécuriser tes routes
  3. Valider les données entrantes : librairie joi ou express-validator
  4. Ajouter des tests : Jest + Supertest pour tester tes endpoints
  5. Déployer sur le cloud : Heroku, Vercel, DigitalOcean, AWS

Opinion tranchée : Apprends PostgreSQL, pas MongoDB. Mongo, c’est bien pour du NoSQL pur, mais 90% des projets ont besoin de relations entre les données. Et SQL, c’est un skill universel. Tu peux faire du NoSQL avec Postgres (jsonb), mais pas l’inverse.

Conclusion

Tu viens de créer ta première API REST avec Node.js et Express. Ces concepts (CRUD, verbes HTTP, routes, middlewares) sont la fondation de toutes les API modernes. Expérimente, ajoute de nouvelles fonctionnalités, et n’hésite pas à casser des choses : c’est en pratiquant qu’on progresse ! 🚀

— Alex Moreau, développeur web freelance. J’ai créé des dizaines d’APIs REST pour mes clients. La première était moche, mais elle marchait. Aujourd’hui, je les structure proprement dès le début. Si tu suis ce tuto, tu es sur la bonne voie.