Lorsque l’on découvre Node.js pour la première fois, on a souvent tendance à tout mettre dans un seul fichier. C’est normal. On teste, on apprend, on essaie de comprendre comment ce fameux moteur JavaScript côté serveur fonctionne.
Cependant, vient rapidement un moment où le projet grossit, les fonctionnalités s’accumulent, et le code commence à devenir confus. On se retrouve avec des fichiers de plusieurs centaines de lignes, difficiles à maintenir, où chaque modification devient une petite aventure pleine de risques.
Structurer proprement votre projet Node.js est une étape essentielle, même si vous débutez. C’est ce qui vous permettra de garder un code clair, facile à améliorer, et compréhensible par vous dans six mois, ainsi que par ceux avec qui vous collaborerez un jour.
- Comprendre comment organiser un projet Node.js pour garder un code clair, lisible et facile à maintenir.
- Savoir créer une architecture évolutive avec routes, contrôleurs, services et modèles pour éviter les fichiers surchargés.
- Apprendre à connecter une base de données et gérer proprement la logique métier afin de pouvoir faire évoluer son application sans tout réécrire.
Dans ce chapitre, nous allons prendre le temps de poser des bases solides. Nous partirons de zéro, en expliquant chaque principe, en donnant des exemples concrets et en vous accompagnant dans la mise en place d’une arborescence saine. Que vous souhaitiez créer une petite API, une application web, ou un service plus complexe, vous pourrez appliquer les conseils qui suivent.
- Pourquoi organiser correctement un projet Node.js ?
- Créer la base : initialiser un projet Node.js proprement
- Comprendre la logique d’une bonne arborescence
- Créer l’architecture interne du projet
- Comprendre la logique des couches
- Intégrer une base de données : un passage souvent redouté
- Aller plus loin dans la structure : middleware et services
- Quelques conventions simples qui changent tout
- Ajouter des helpers : les petits outils du quotidien
- Mettre en place une structure finale propre et claire
- Bonnes pratiques et conventions professionnelles
Pourquoi organiser correctement un projet Node.js ?
Travailler sans structure, c’est un peu comme ranger des outils dans une seule boîte sans séparation. Au début, tout tient parfaitement. Puis, au fur et à mesure, vous commencez à perdre du temps à chercher le bon tournevis, vous trouvez un marteau posé sur une clé Allen, et rapidement, la simple idée de bricoler vous fatigue.
Dans le développement, c’est pareil. Une structure claire apporte plusieurs avantages très concrets.
Premièrement, vous gagnez en lisibilité. Un projet bien organisé permet de voir rapidement ce qui se passe, où sont les routes, où sont les contrôleurs, où se trouve la configuration de la base de données.
Deuxièmement, vous gagnez en réutilisabilité. Quand une partie du code est rangée dans un fichier cohérent, vous pouvez la modifier, la tester, ou la remplacer sans casser le reste de votre projet.
Troisièmement, cela réduit les risques d’erreurs. Plus votre code est découpé en modules cohérents, moins il y a de dépendances involontaires entre les parties.
Et pour finir, c’est aussi une preuve de professionnalisme, que vous travailliez seul ou dans une équipe. Un développeur qui ouvre votre projet doit comprendre son fonctionnement en quelques secondes. C’est l’objectif.
Créer la base : initialiser un projet Node.js proprement
Nous allons commencer à zéro. Supposons que vous voulez créer un nouveau projet. Sur votre ordinateur, commencez par créer un dossier vide. Par exemple :
mon-projet-nodeEnsuite, ouvrez ce dossier dans votre terminal, puis lancez :
npm init -yCette commande crée un fichier package.json, qui est en quelque sorte la carte d’identité de votre projet. Il indique son nom, sa version, les dépendances installées, les scripts que vous pouvez exécuter, etc.
Une fois cela fait, créons un premier fichier :
server.jsCe fichier servira de point d’entrée à votre application. Pour le moment, mettons-y simplement :
console.log('Serveur lancé');Puis testez :
node server.jsSi cela affiche bien votre message, c’est que tout fonctionne. Rien de compliqué jusque-là.
Cependant, nous n’allons pas laisser notre application dans ce seul fichier. Ce serait comme écrire tout un roman dans un seul paragraphe. À mesure que notre code grandira, nous allons le découper.
Pour ceux qui débutent, Comprendre en installer Node.js.
Comprendre la logique d’une bonne arborescence
Il existe plusieurs façons d’organiser un projet Node.js. La structure exacte dépend du projet. Mais une organisation générale revient souvent, car elle est simple, logique et évolutive.
On y retrouve généralement des dossiers séparés selon leur rôle. Par exemple, un dossier pour les routes, un dossier pour les contrôleurs, un autre pour les modèles si l’on utilise une base de données, un dossier pour les fonctions utilitaires, un dossier pour la configuration.
Avant de construire tout cela, voyons une arborescence très simple :
mon-projet-node/
│
├── server.js
├── package.json
│
├── src/
│ ├── routes/
│ ├── controllers/
│ ├── models/
│ └── config/
│
└── README.mdLe dossier src contiendra tout le code de votre application. Cela permet de le séparer des fichiers liés aux dépendances et à la configuration.
Les routes sont les chemins qui définissent quelles actions sont disponibles via HTTP. Par exemple : obtenir une liste d’articles, créer un utilisateur, etc.
Les contrôleurs contiennent la logique de traitement associée aux routes. Ils reçoivent les demandes, font les vérifications, récupèrent ou envoient des données, et renvoient une réponse.
Les modèles représentent souvent les données. Si vous utilisez MongoDB ou MySQL, par exemple, ce sont les fichiers qui décrivent la structure de vos entités.
Le dossier config accueille les réglages : connexion à la base, variables d’environnement, ports, clés secrètes, etc.
Cette organisation n’est pas gravée dans le marbre, mais elle permet de partir sur des bases solides.
Dans la vie réelle, des développeurs ont parfois eu peur d’ouvrir un vieux projet non structuré, tellement les fichiers étaient entassés sans logique. Il m’est arrivé une fois d’ouvrir un projet où tout était dans un seul fichier de 3100 lignes. Oui, 3100. On avait l’impression de lire une épopée médiévale, mais sans la poésie. L’arborescence, c’est ce qui vous évite ça.
Mettre en place les premières briques : Express, routes et contrôleurs
Pour commencer à structurer un projet Node.js correctement, nous allons utiliser le framework Express. Ce n’est pas obligatoire, mais il est tellement répandu que cela facilitera grandement votre apprentissage. De nombreux projets professionnels reposent sur Express, ce qui signifie que vous apprendrez à travailler comme de très nombreux développeurs.
Dans votre terminal, toujours dans le dossier du projet, installez Express :
npm install expressEnsuite, modifions notre fichier server.js pour y lancer un serveur HTTP basique :
const express = require('express');
const app = express();
app.use(express.json());
app.get('/', (req, res) => {
res.send('Bienvenue sur notre projet Node.js');
});
const PORT = 3000;
app.listen(PORT, () => {
console.log(`Serveur en cours d'exécution sur https://localhost:${PORT}`);
});À ce stade, si vous lancez :
node server.jspuis que vous ouvrez votre navigateur sur https://localhost:3000, vous devriez voir le message de bienvenue. Tout fonctionne.
Cependant, nous n’allons pas ajouter nos routes directement dans server.js. Ce fichier doit rester simple. Il sert uniquement à démarrer l’application. Pour tout le reste, nous allons déplacer la logique dans le dossier src.
Créer l’architecture interne du projet
Dans votre projet, créez un dossier src :
mon-projet-node/
│
├── src/
│ ├── routes/
│ ├── controllers/
│ ├── models/
│ └── config/Pour illustrer, créons un exemple très simple. Imaginons que nous voulons gérer une liste d’utilisateurs. Pas encore besoin de base de données. Nous allons simplement renvoyer une liste fictive.
Dans src/controllers, créez un fichier userController.js :
const users = [
{ id: 1, name: "Alice", email: "[email protected]" },
{ id: 2, name: "Marc", email: "[email protected]" }
];
exports.getUsers = (req, res) => {
res.json(users);
};Ce fichier contient la logique : obtenir les utilisateurs et les renvoyer. Rien de plus. C’est propre. C’est clair.
Ensuite, dans src/routes, créez un fichier userRoutes.js :
const express = require('express');
const router = express.Router();
const userController = require('../controllers/userController');
router.get('/users', userController.getUsers);
module.exports = router;Ici, nous définissons l’URL /users et nous appelons la fonction du contrôleur associée.
Il ne reste plus qu’à dire à notre serveur d’utiliser ces routes. Modifiez server.js :
const express = require('express');
const app = express();
const userRoutes = require('./src/routes/userRoutes');
app.use(express.json());
app.use('/api', userRoutes);
const PORT = 3000;
app.listen(PORT, () => {
console.log(`Serveur en cours d'exécution sur https://localhost:${PORT}`);
});Maintenant, relancez votre serveur, rendez-vous sur :
https://localhost:3000/api/usersVous devriez voir s’afficher votre liste d’utilisateurs.
En quelques fichiers, vous avez maintenant :
- Un point d’entrée clair (
server.js) - Des routes regroupées (
userRoutes.js) - Une logique métier séparée (
userController.js)
Vous venez de franchir une étape essentielle : vous avez structuré votre code pour qu’il soit compréhensible, maintenable et évolutif.
Comprendre la logique des couches
Ce découpage en routes, contrôleurs et modèles est inspiré du modèle MVC (Modèle, Vue, Contrôleur). Même si toutes les applications Node.js ne suivent pas strictement ce modèle, l’esprit reste le même : séparer les responsabilités.
La route sert de porte d’entrée. Elle dit « quelqu’un veut accéder à telle fonctionnalité ».
Le contrôleur décide quoi faire avec cette demande. Il peut récupérer des données, vérifier des conditions, préparer une réponse.
Le modèle, lui, s’occupe des données. Quand on ajoutera une base de données, vous comprendrez combien cette séparation simplifie la vie. On modifie la base, sans toucher à la logique métier.
En résumé, chaque fichier a un rôle. On évite d’avoir un fichier qui fait tout. C’est exactement ce qui rend le code lisible.
Un développeur expérimenté reconnaît immédiatement un projet bien organisé. Quand un autre développeur ouvre votre projet, il doit pouvoir deviner où se trouve chaque partie du code sans vous poser de question. C’est ça, la qualité.
Pour allez plus loin, Comprendre l’architecture MVC d’un site web.
Ajouter la configuration : variables d’environnement et paramètres
Dans un projet plus sérieux, on ne va pas écrire directement des valeurs importantes dans le code. Le port du serveur, les clés API, l’URL de la base de données, tout cela doit être mis dans des variables d’environnement.
Pour cela, installons dotenv (npm, package et module javascript) :
npm install dotenvCréez ensuite à la racine du projet un fichier .env :

Des formations informatique pour tous !
Débutant ou curieux ? Apprenez le développement web, le référencement, le webmarketing, la bureautique, à maîtriser vos appareils Apple et bien plus encore…
Formateur indépendant, professionnel du web depuis 2006, je vous accompagne pas à pas et en cours particulier, que vous soyez débutant ou que vous souhaitiez progresser. En visio, à votre rythme, et toujours avec pédagogie.
Découvrez mes formations Qui suis-je ?PORT=3000Et dans votre fichier server.js :
require('dotenv').config();
const express = require('express');
const app = express();
const userRoutes = require('./src/routes/userRoutes');
app.use(express.json());
app.use('/api', userRoutes);
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Serveur lancé sur https://localhost:${PORT}`);
});Vous pouvez maintenant modifier le port sans toucher au code.
Cela peut sembler anodin, mais en production, lorsque vous déploierez votre application, ce système vous évitera bien des ennuis. Changer un paramètre ne doit jamais nécessiter de modifier le code source.
Un jour, dans une petite équipe débutante, le port du serveur était codé en dur dans cinq fichiers différents. Lorsque l’équipe a dû changer ce port pour des raisons techniques, ils ont dû chercher partout dans le projet. Ils en ont oublié un fichier. L’application fonctionnait à moitié. Résultat : une journée entière pour un changement qui aurait dû prendre 10 secondes. Une seule variable .env aurait évité ce chaos.
Intégrer une base de données : un passage souvent redouté
Jusqu’ici, notre projet renvoie simplement une liste d’utilisateurs figée dans un tableau. C’est utile pour comprendre la logique, mais dans la majorité des projets Node.js, vous aurez besoin d’une base de données. C’est elle qui va permettre de stocker des informations et de les récupérer de manière durable.
Dans l’écosystème Node.js, deux choix reviennent souvent : MySQL (base relationnelle) et MongoDB (base NoSQL). Les deux sont très utilisés et très valides. Pour garder les choses simples et progressives, nous allons utiliser MongoDB avec Mongoose, car son fonctionnement est particulièrement accessible pour un débutant.
Installer et connecter MongoDB avec Mongoose
D’abord, installez Mongoose :
npm install mongooseEnsuite, dans src/config, créez un fichier database.js :
const mongoose = require('mongoose');
const connectToDatabase = async () => {
try {
await mongoose.connect(process.env.MONGO_URI, {
useNewUrlParser: true,
useUnifiedTopology: true
});
console.log('Connexion à MongoDB réussie');
} catch (error) {
console.error('Erreur de connexion à MongoDB:', error);
}
};
module.exports = connectToDatabase;Ce fichier a une seule responsabilité : gérer la connexion à la base. Encore une fois, la séparation des rôles est primordiale.
Dans votre fichier .env, ajoutez :
MONGO_URI=mongodb://localhost:27017/mon_projet_nodeEnfin, dans server.js, activez la connexion :
require('dotenv').config();
const express = require('express');
const app = express();
const connectToDatabase = require('./src/config/database');
const userRoutes = require('./src/routes/userRoutes');
connectToDatabase();
app.use(express.json());
app.use('/api', userRoutes);
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Serveur opérationnel sur https://localhost:${PORT}`);
});Vous pouvez lancer votre serveur. Si MongoDB fonctionne sur votre machine, vous devriez voir le message « Connexion à MongoDB réussie ».
Créer un modèle : donner une forme aux données
Votre application a désormais accès à une vraie base de données. Mais stocker des données sans structure peut rapidement devenir dangereux. C’est là que les modèles entrent en jeu.
Un modèle permet de définir la forme que doivent avoir les données. Comme une fiche d’identification.
Dans src/models, créez User.js :
const mongoose = require('mongoose');
const userSchema = new mongoose.Schema({
name: { type: String, required: true },
email: { type: String, required: true, unique: true }
});
module.exports = mongoose.model('User', userSchema);Ce schéma indique très simplement que chaque utilisateur possède un nom et un email.
Modifier le contrôleur pour utiliser la base de données
Nous allons maintenant remplacer notre tableau fictif par une vraie requête vers MongoDB.
Dans src/controllers/userController.js :
const User = require('../models/User');
exports.getUsers = async (req, res) => {
try {
const users = await User.find();
res.json(users);
} catch (error) {
res.status(500).json({ error: 'Erreur lors de la récupération des utilisateurs' });
}
};Vous venez d’effectuer votre première requête réelle avec Node.js et MongoDB. Si vous ajoutez des utilisateurs dans votre base, ils s’afficheront automatiquement. Et si vous ne savez pas encore comment les ajouter, pas d’inquiétude : cela vient juste après.
Ajouter une route pour créer un utilisateur
Dans userRoutes.js, ajoutez :
router.post('/users', async (req, res) => {
try {
const { name, email } = req.body;
const newUser = await User.create({ name, email });
res.json(newUser);
} catch (error) {
res.status(400).json({ error: 'Impossible de créer l’utilisateur' });
}
});Vous pouvez maintenant envoyer une requête POST (via Postman, Thunder Client, ou Insomnia) avec un corps JSON :
{
"name": "Jeanne",
"email": "[email protected]"
}Votre utilisateur sera réellement enregistré. Ce n’est plus du faux. C’est du concret.
Aller plus loin dans la structure : middleware et services
À ce stade, votre projet Node.js prend forme. Pourtant, en grandissant, votre code peut vite devenir lourd si vous n’anticipez pas une séparation logique supplémentaire.
Un middleware est une fonction qui s’exécute entre la requête du client et la réponse. Il sert à faire des vérifications transversales : authentification, journalisation, validation, etc.
Par exemple, pour vérifier qu’un utilisateur a fourni un nom avant d’être créé, nous aurions pu écrire cette vérification directement dans la route. Mais une meilleure approche est de créer un fichier middleware, par exemple :
Dans src/middleware/validateUser.js :
module.exports = (req, res, next) => {
const { name, email } = req.body;
if (!name || !email) {
return res.status(400).json({ error: 'Nom et email obligatoires' });
}
next();
};Puis l’utiliser dans la route :
const validateUser = require('../middleware/validateUser');
router.post('/users', validateUser, userController.createUser);Ici, la route devient plus claire. Le contrôleur ne traite que la logique. Le middleware se charge des vérifications. La structure reste propre.
Quelques conventions simples qui changent tout
Organiser son projet Node.js, ce n’est pas uniquement créer des dossiers. C’est aussi adopter des conventions que vous garderez toute votre carrière.
- Utilisez des noms clairs. Ne raccourcissez pas « controller » en
ctrlparce que c’est plus court. Votre futur vous vous remerciera. - Ne mélangez pas français et anglais dans vos fichiers. Choisissez une langue et gardez-la. En général : tout en anglais.
- Ne mettez jamais d’informations sensibles dans le code. Jamais. Pas d’identifiants, pas de mot de passe, pas de clés API.
- Commentez seulement ce qui a besoin de l’être. Un commentaire utile explique pourquoi, pas ce que fait la ligne. Un bon code se lit presque comme une phrase.
- Et surtout : prenez le temps de relire. Une seconde lecture révèle souvent ce qui peut être simplifié.
Structurer davantage : services, helpers et organisation logique
À mesure que votre application Node.js grandit, vous allez ajouter de nouvelles fonctionnalités. Si vous mettez toute la logique dans les contrôleurs, ceux-ci deviendront volumineux. Ils finiront par ressembler à ces romans qu’on commence avec enthousiasme, mais qu’on peine à finir parce qu’ils sont trop denses.
Pour éviter cela, on introduit un nouveau niveau dans notre architecture : les services.
Les services sont des fichiers qui contiennent la logique métier plus complexe. Les contrôleurs appellent les services pour obtenir un résultat. Cela permet de garder vos contrôleurs simples, presque comme des messagers : ils reçoivent une requête, la transmettent au service, puis renvoient la réponse.
Créons un service simple.
Dans src/services, créez userService.js :
const User = require('../models/User');
exports.getAllUsers = async () => {
return await User.find();
};
exports.createUser = async (name, email) => {
return await User.create({ name, email });
};Nous avons déplacé la logique métier ici. Ensuite, modifions notre contrôleur pour l’utiliser.
src/controllers/userController.js :
const userService = require('../services/userService');
exports.getUsers = async (req, res) => {
try {
const users = await userService.getAllUsers();
res.json(users);
} catch (error) {
res.status(500).json({ error: 'Erreur lors de la récupération des utilisateurs' });
}
};
exports.createUser = async (req, res) => {
try {
const { name, email } = req.body;
const newUser = await userService.createUser(name, email);
res.json(newUser);
} catch (error) {
res.status(400).json({ error: 'Impossible de créer l’utilisateur' });
}
};Puis, mettons à jour notre route POST dans userRoutes.js :
const validateUser = require('../middleware/validateUser');
router.post('/users', validateUser, userController.createUser);Vous voyez comment les responsabilités sont découpées :
- Les routes gèrent les chemins URL.
- Les contrôleurs gèrent la réception et l’envoi de réponses.
- Les services gèrent la logique de manipulation des données.
- Les modèles représentent la structure des données.
- Les middlewares gèrent les vérifications transversales.
Votre projet reste clair, propre et compréhensible. Et surtout, il peut continuer de grandir.
Ajouter des helpers : les petits outils du quotidien
À un moment ou à un autre, vous aurez besoin de petites fonctions utilitaires. Par exemple, formater une date, générer un identifiant, nettoyer une chaîne de caractères.
Plutôt que de les disséminer partout dans votre code, créez un dossier helpers dans src :
src/
helpers/
formatDate.jsformatDate.js :
exports.formatDate = (date) => {
return new Date(date).toLocaleDateString('fr-FR');
};Vous pourrez l’utiliser dans n’importe quel fichier :
const { formatDate } = require('../helpers/formatDate');
console.log(formatDate(Date.now()));Les helpers sont comme des petits outils de poche. On ne les remarque pas toujours, mais lorsqu’ils sont bien rangés, ils font gagner du temps.
Mettre en place une structure finale propre et claire
À ce stade, votre projet commence à ressembler à ceci :
mon-projet-node/
│
├── server.js
├── package.json
├── .env
│
└── src/
├── routes/
│ └── userRoutes.js
├── controllers/
│ └── userController.js
├── services/
│ └── userService.js
├── models/
│ └── User.js
├── middleware/
│ └── validateUser.js
├── helpers/
│ └── formatDate.js
└── config/
└── database.jsIl est important de prendre un moment ici pour observer ce que vous venez d’accomplir. Vous êtes passé d’un simple fichier Node.js renvoyant un message à une application structurée, modulaire, claire, et surtout professionnelle. Beaucoup de développeurs mettent des mois à comprendre cette logique. Vous êtes en train de la maîtriser.
Bonnes pratiques et conventions professionnelles
Maintenant que la base est là, voici quelques habitudes précieuses à adopter.
- Nommez vos fichiers et dossiers en minuscules, en anglais, et avec des noms explicites. Par exemple
userRoutes.jsplutôt queUR.js. - Ne mettez jamais de logique dans
server.js. Il démarre l’application, et c’est tout. - Séparez les responsabilités. Si une fonction fait trop de choses, scindez-la.
- Relisez votre code à voix haute. Cela peut paraître étrange, mais si une phrase ne « sonne » pas naturel, elle mérite sûrement d’être simplifiée.
- Gardez un style de code cohérent. Par exemple, choisissez entre guillemets simples ou doubles, mais ne mélangez pas.
- Utilisez un formateur automatique comme Prettier. Cela enlève une grande partie du « bruit » dans les discussions de code.
Vous n’avez pas besoin d’être un expert pour adopter de bonnes pratiques. Vous avez simplement besoin d’être régulier.
Vous avez posé les fondations d’un vrai projet
Vous venez de voir comment structurer un projet Node.js pour qu’il soit lisible, maintenable et évolutif. Vous avez appris à organiser vos routes, vos contrôleurs, vos modèles, vos services, et à séparer proprement les responsabilités.
Ce que vous avez construit ici est une base solide, sur laquelle vous pourrez ajouter de nouvelles fonctionnalités : authentification, pagination, uploads de fichiers, envoi d’e-mails, intégration de services externes.
Plus vous pratiquerez, plus ces concepts deviendront naturels. Au début, organiser son projet peut sembler être un effort supplémentaire. Mais très vite, vous prendrez conscience que c’est ce qui vous permet d’avancer loin, sans vous épuiser ni vous perdre.
Le développement n’est pas une course. C’est un chemin. À votre rythme. Avec curiosité. Et avec plaisir. Si vous prenez soin de la structure, votre code prendra soin de vous.

