Express.js
Un framework web minimaliste et flexible pour Node.js qui permet de créer rapidement des API robustes et des applications web performantes.
Qu'est-ce que Express.js ?
Imaginez que vous construisiez une maison. Vous pourriez fabriquer chaque outil et chaque matériau vous-même, ou bien utiliser des outils spécialisés et des matériaux prêts à l'emploi pour accélérer considérablement la construction.
Express.js est comme une boîte à outils complète pour les développeurs qui souhaitent construire des sites web et des applications avec Node.js. C'est un framework qui fournit des solutions prêtes à l'emploi pour les défis courants du développement web, sans imposer de structure rigide.
Pourquoi Express.js est si populaire ?
Simplicité et rapidité
Express.js permet de démarrer rapidement un projet, avec une courbe d'apprentissage douce et une absence de configuration complexe.
Flexibilité
Contrairement à d'autres frameworks qui imposent une structure rigide, Express.js laisse les développeurs organiser leur code comme ils le souhaitent.
En résumé, Express.js est la solution idéale pour créer des sites web, des applications et des API modernes, en simplifiant considérablement le processus de développement tout en offrant la flexibilité nécessaire pour s'adapter à différents besoins.
Fonctionnement technique
Express.js est un framework web minimaliste pour Node.js qui facilite la création d'applications et d'API web. Il repose sur le module HTTP de Node.js et ajoute une couche d'abstraction pour simplifier les tâches courantes du développement web.
Mise en place d'un serveur de base
Serveur Express.js minimal
Voici comment démarrer rapidement un serveur Express.js :
const express = require('express');
const app = express();
const port = process.env.PORT || 3000;
// Middleware pour parser le JSON
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
// Route simple
app.get('/', (req, res) => {
res.send('Bienvenue sur notre API Express.js !');
});
// Démarrage du serveur
app.listen(port, () => {
console.log(`Serveur démarré sur http://localhost:${port}`);
});
Routage
Le routage est l'un des aspects fondamentaux d'Express.js. Il permet de définir comment l'application répond aux requêtes client pour un endpoint et une méthode HTTP spécifiques.
// routes/users.js
const express = require('express');
const router = express.Router();
// Liste tous les utilisateurs
router.get('/', (req, res) => {
res.json({ users: [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' },
{ id: 3, name: 'Charlie' }
]});
});
// Récupère un utilisateur par ID
router.get('/:id', (req, res) => {
const userId = parseInt(req.params.id);
// Simulation d'accès à une base de données
const user = { id: userId, name: `Utilisateur ${userId}` };
res.json(user);
});
// Crée un nouvel utilisateur
router.post('/', (req, res) => {
// Validation des données
if (!req.body.name) {
return res.status(400).json({ error: 'Le nom est requis' });
}
// Simulation de création en base de données
const newUser = {
id: 4,
name: req.body.name,
createdAt: new Date()
};
res.status(201).json(newUser);
});
// Exporte le router pour l'utiliser dans l'application principale
module.exports = router;
// Dans app.js
const usersRouter = require('./routes/users');
app.use('/api/users', usersRouter);
Middleware
Les middlewares sont des fonctions qui ont accès à l'objet de requête (req), à l'objet de réponse (res) et à la fonction suivante dans le cycle requête-réponse. Ils peuvent exécuter du code, modifier les objets de requête et de réponse, terminer le cycle, ou appeler le prochain middleware.
// Middleware d'authentification
const authenticate = (req, res, next) => {
const authHeader = req.headers.authorization;
if (!authHeader || !authHeader.startsWith('Bearer ')) {
return res.status(401).json({ error: 'Token d'authentification manquant' });
}
const token = authHeader.split(' ')[1];
try {
// Dans un cas réel, on vérifierait le token JWT
// const user = jwt.verify(token, process.env.JWT_SECRET);
// req.user = user;
// Simulation pour l'exemple
req.user = { id: 1, role: 'admin' };
next();
} catch (error) {
res.status(401).json({ error: 'Token invalide' });
}
};
// Middleware pour vérifier les droits administrateur
const isAdmin = (req, res, next) => {
if (req.user && req.user.role === 'admin') {
next();
} else {
res.status(403).json({ error: 'Accès refusé' });
}
};
// Utilisation des middlewares sur une route sensible
app.get('/api/admin/dashboard', authenticate, isAdmin, (req, res) => {
res.json({ message: 'Bienvenue sur le dashboard administrateur', user: req.user });
});
Gestion des erreurs
Express.js permet une gestion centralisée des erreurs grâce à un middleware spécial qui prend quatre arguments (err, req, res, next).
// Middleware de gestion globale des erreurs
app.use((err, req, res, next) => {
console.error(err.stack);
// Erreurs opérationnelles vs erreurs de programmation
if (err.isOperational) {
return res.status(err.statusCode || 500).json({
status: 'error',
message: err.message
});
}
// Erreurs inattendues - ne pas exposer les détails en production
res.status(500).json({
status: 'error',
message: process.env.NODE_ENV === 'production'
? 'Une erreur interne est survenue'
: err.message
});
});
// Class d'erreur personnalisée
class AppError extends Error {
constructor(message, statusCode) {
super(message);
this.statusCode = statusCode;
this.isOperational = true;
Error.captureStackTrace(this, this.constructor);
}
}
// Exemple d'utilisation dans une route
app.get('/api/products/:id', async (req, res, next) => {
try {
const product = await Product.findById(req.params.id);
if (!product) {
return next(new AppError('Produit non trouvé', 404));
}
res.json(product);
} catch (err) {
next(err);
}
});
Architecture MVC
Bien qu'Express.js ne force pas l'utilisation d'un pattern spécifique, il est courant d'organiser les applications avec le pattern MVC (Modèle-Vue-Contrôleur).
// models/userModel.js
const mongoose = require('mongoose');
const bcrypt = require('bcrypt');
const userSchema = new mongoose.Schema({
email: {
type: String,
required: [true, 'Un email est requis'],
unique: true,
lowercase: true
},
password: {
type: String,
required: [true, 'Un mot de passe est requis'],
minlength: 8,
select: false
},
name: {
type: String,
required: [true, 'Un nom est requis']
},
createdAt: {
type: Date,
default: Date.now
}
});
// Méthode pour comparer des mots de passe
userSchema.methods.checkPassword = async function(candidatePassword, userPassword) {
return await bcrypt.compare(candidatePassword, userPassword);
};
// Hashage du mot de passe avant sauvegarde
userSchema.pre('save', async function(next) {
if (!this.isModified('password')) return next();
this.password = await bcrypt.hash(this.password, 12);
next();
});
const User = mongoose.model('User', userSchema);
module.exports = User;
// controllers/authController.js
const User = require('../models/userModel');
const jwt = require('jsonwebtoken');
const { promisify } = require('util');
exports.signup = async (req, res, next) => {
try {
const newUser = await User.create({
name: req.body.name,
email: req.body.email,
password: req.body.password
});
const token = jwt.sign(
{ id: newUser._id },
process.env.JWT_SECRET,
{ expiresIn: process.env.JWT_EXPIRES_IN }
);
res.status(201).json({
status: 'success',
token,
data: {
user: {
id: newUser._id,
name: newUser.name,
email: newUser.email
}
}
});
} catch (err) {
next(err);
}
};
// routes/authRoutes.js
const express = require('express');
const authController = require('../controllers/authController');
const router = express.Router();
router.post('/signup', authController.signup);
router.post('/login', authController.login);
module.exports = router;
Intégration avec des API externes
Express.js facilite la création de services qui interagissent avec des API tierces :
// services/weatherService.js
const axios = require('axios');
exports.getWeatherByCity = async (city) => {
try {
const response = await axios.get(`https://api.openweathermap.org/data/2.5/weather`, {
params: {
q: city,
appid: process.env.WEATHER_API_KEY,
units: 'metric'
}
});
return {
city: response.data.name,
country: response.data.sys.country,
temperature: response.data.main.temp,
description: response.data.weather[0].description,
icon: response.data.weather[0].icon,
humidity: response.data.main.humidity,
windSpeed: response.data.wind.speed
};
} catch (error) {
if (error.response && error.response.status === 404) {
throw new Error('Ville non trouvée');
}
throw new Error('Erreur lors de la récupération des données météo');
}
};
// controllers/weatherController.js
const weatherService = require('../services/weatherService');
exports.getWeather = async (req, res, next) => {
try {
const { city } = req.params;
if (!city) {
return res.status(400).json({
status: 'error',
message: 'Veuillez fournir un nom de ville'
});
}
const weatherData = await weatherService.getWeatherByCity(city);
res.json({
status: 'success',
data: weatherData
});
} catch (err) {
next(err);
}
};
// routes/weatherRoutes.js
const express = require('express');
const weatherController = require('../controllers/weatherController');
const router = express.Router();
router.get('/:city', weatherController.getWeather);
module.exports = router;
Avantages techniques
- Performance - Express.js est léger et fonctionne sur la plateforme asynchrone de Node.js
- Écosystème riche - Accès à une vaste collection de middlewares pour diverses fonctionnalités
- Routage puissant - Système de routage flexible qui prend en charge les paramètres et les modèles
- Templates - Support pour de nombreux moteurs de templates comme Pug, EJS, Handlebars
- Middleware modulaire - Système de middleware extensible pour personnaliser le comportement de l'application
Cas d'usage idéaux
- APIs RESTful - Création rapide d'API scalables et maintenables
- Applications en temps réel - Combiné avec Socket.io pour des fonctionnalités en temps réel
- Applications single-page - Backend pour des SPA développés avec
React, Angular ou Vue.js
- Microservices - Développement de services légers et indépendants
- Prototypes rapides - Mise en place rapide de MVPs et de preuves de concept
Cas d'usage
APIs RESTful
Express.js excelle dans la création d'APIs REST performantes et bien structurées, avec un routage clair et un support JSON natif.
Applications en temps réel
Combiné avec Socket.io, Express.js est idéal pour créer des applications de chat, des tableaux de bord en direct, ou des jeux multijoueurs avec des mises à jour instantanées.
Backends pour SPAs
Parfait comme backend léger pour les applications single-page développées avec React, Angular ou
Vue.js.
Microservices
La légèreté et la flexibilité d'Express.js en font un choix excellent pour développer des microservices spécifiques dans une architecture distribuée.
Entreprises qui utilisent Express.js
De nombreuses entreprises de premier plan font confiance à Express.js pour leurs applications :