Pourquoi Microservices avec Express ?
Au quotidien, un développeur a besoin d'implémenter des architectures complexes pour répondre aux besoins croissants des applications modernes. Les microservices sont une solution efficace qui permettent de diviser les applications en composants indépendants et autonomes. Chaque service peut être développé, déployé et évoluer à son propre rythme, ce qui augmente la flexibilité et l'évolutivité de l'application.
Un cas d'utilisation concret est une application de e-commerce où chaque fonctionnalité (panier, paiement, catalogue) peut être gérée par un service distinct. Cette architecture permet d'améliorer le temps de réponse en évitant les dépendances entre les services et facilite le scaling indépendant.
Prerequis
Avant de commencer ce tutoriel, vous devriez avoir les connaissances suivantes :
- Connaissance avancée de JavaScript
- Expérience avec Node.js et Express
- Familiarité avec la gestion des dépendances (npm)
- Compréhension des bases de l'architecture microservices
Outils à installer :
- Node.js >= 14.0.0
- npm >= 6.0.0
- MongoDB ou une base de données similaire pour stocker les données
Concepts fondamentaux
1. Microservices
Un microservice est un composant indépendant et autonome qui effectue une fonction spécifique. Les microservices communiquent entre eux via des API RESTful.
// Importation des modules nécessaires
const express = require('express');
const app = express();
// Route pour obtenir la liste des tâches
app.get('/tasks', (req, res) => {
// Logique pour récupérer les tâches depuis une base de données
const tasks = ['Task 1', 'Task 2', 'Task 3'];
res.json(tasks);
});
// Route pour ajouter une nouvelle tâche
app.post('/tasks', (req, res) => {
// Logique pour ajouter la nouvelle tâche
const task = req.body.task;
console.log(`Nouvelle tâche ajoutée : ${task}`);
res.status(201).json({ message: 'Tâche ajoutée avec succès' });
});
// Démarrage du serveur
app.listen(3000, () => {
console.log('Serveur démarré sur le port 3000');
});
2. API Gateway
L'API Gateway est une couche d'entrée qui centralise les requêtes vers différents services microservices. Elle gère également la sécurité et la performance.
// Importation des modules nécessaires
const express = require('express');
const { createProxyMiddleware } = require('http-proxy-middleware');
const app = express();
// Configuration de l'API Gateway
app.use('/tasks', createProxyMiddleware({ target: 'http://localhost:3001', changeOrigin: true }));
app.use('/users', createProxyMiddleware({ target: 'http://localhost:3002', changeOrigin: true }));
// Démarrage du serveur
app.listen(8000, () => {
console.log('API Gateway démarrée sur le port 8000');
});
3. Docker
Docker permet de containeriser les applications microservices, facilitant leur déploiement et la mise en production.
## Création d'un fichier Dockerfile pour le service tasks
FROM node:14-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
CMD ["node", "server.js"]
bash
## Création d'un docker-compose.yml
version: '3'
services:
tasks:
build: ./tasks
ports:
- "3001:3000"
users:
build: ./users
ports:
- "3002:3000"
4. Service Discovery
La découverte de services permet aux microservices de se localiser et de communiquer entre eux.
## Installation du module consul-client
npm install consul-client
// Importation des modules nécessaires
const Consul = require('consul-client');
const consul = new Consul();
// Enregistrement du service tasks
consul.agent.service.register({
ID: 'tasks-service',
Name: 'tasks',
Port: 3001,
}, (err) => {
if (err) throw err;
console.log('Service registered successfully');
});
Mise en pratique : projet fil rouge
Étape 1 : Création du service tasks
Créer un nouveau répertoire tasks et initialiser un nouveau projet Node.js.
mkdir tasks
cd tasks
npm init -y
npm install express mongoose dotenv
Structure de fichiers :
tasks/
├── Dockerfile
├── package.json
├── server.js
└── .env
Contenu du server.js :
// Importation des modules nécessaires
const express = require('express');
const mongoose = require('mongoose');
// Connexion à la base de données MongoDB
mongoose.connect(process.env.MONGO_URI, {
useNewUrlParser: true,
useUnifiedTopology: true,
}, (err) => {
if (err) throw err;
console.log('Connecté à la base de données');
});
// Modèle de tâche
const Task = mongoose.model('Task', new mongoose.Schema({
name: String,
}));
// Application Express
const app = express();
app.use(express.json());
// Route pour obtenir la liste des tâches
app.get('/tasks', async (req, res) => {
try {
const tasks = await Task.find({});
res.json(tasks);
} catch (err) {
res.status(500).json({ message: err.message });
}
});
// Route pour ajouter une nouvelle tâche
app.post('/tasks', async (req, res) => {
const task = new Task({
name: req.body.name,
});
try {
await task.save();
res.status(201).json({ message: 'Tâche ajoutée avec succès' });
} catch (err) {
res.status(400).json({ message: err.message });
}
});
// Démarrage du serveur
app.listen(process.env.PORT || 3000, () => {
console.log('Serveur démarré sur le port ' + (process.env.PORT || 3000));
});
Contenu du Dockerfile :
FROM node:14-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
CMD ["node", "server.js"]
Contenu du .env :
MONGO_URI=mongodb://localhost/tasksdb
PORT=3000
Étape 2 : Création de l'API Gateway
Créer un nouveau répertoire api-gateway et initialiser un nouveau projet Node.js.
mkdir api-gateway
cd api-gateway
npm init -y
npm install express http-proxy-middleware
Structure de fichiers :
api-gateway/
├── Dockerfile
├── package.json
└── server.js
Contenu du server.js :
// Importation des modules nécessaires
const express = require('express');
const { createProxyMiddleware } = require('http-proxy-middleware');
const app = express();
// Configuration de l'API Gateway
app.use('/tasks', createProxyMiddleware({ target: 'http://localhost:3001', changeOrigin: true }));
app.use('/users', createProxyMiddleware({ target: 'http://localhost:3002', changeOrigin: true }));
// Démarrage du serveur
app.listen(8000, () => {
console.log('API Gateway démarrée sur le port 8000');
});
Contenu du Dockerfile :
FROM node:14-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
CMD ["node", "server.js"]
Étape 3 : Création du service users
Créer un nouveau répertoire users et initialiser un nouveau projet Node.js.
mkdir users
cd users
npm init -y
npm install express mongoose dotenv
Structure de fichiers :
users/
├── Dockerfile
├── package.json
└── server.js
Contenu du server.js :
// Importation des modules nécessaires
const express = require('express');
const mongoose = require('mongoose');
// Connexion à la base de données MongoDB
mongoose.connect(process.env.MONGO_URI, {
useNewUrlParser: true,
useUnifiedTopology: true,
}, (err) => {
if (err) throw err;
console.log('Connecté à la base de données');
});
// Modèle d'utilisateur
const User = mongoose.model('User', new mongoose.Schema({
name: String,
email: String,
}));
// Application Express
const app = express();
app.use(express.json());
// Route pour obtenir la liste des utilisateurs
app.get('/users', async (req, res) => {
try {
const users = await User.find({});
res.json(users);
} catch (err) {
res.status(500).json({ message: err.message });
}
});
// Route pour ajouter un nouvel utilisateur
app.post('/users', async (req, res) => {
const user = new User({
name: req.body.name,
email: req.body.email,
});
try {
await user.save();
res.status(201).json({ message: 'Utilisateur ajouté avec succès' });
} catch (err) {
res.status(400).json({ message: err.message });
}
});
// Démarrage du serveur
app.listen(process.env.PORT || 3000, () => {
console.log('Serveur démarré sur le port ' + (process.env.PORT || 3000));
});
Contenu du Dockerfile :
FROM node:14-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
CMD ["node", "server.js"]
Étape 4 : Déploiement avec Docker Compose
Créer un fichier docker-compose.yml à la racine du projet.
version: '3'
services:
tasks:
build: ./tasks
ports:
- "3001:3000"
users:
build: ./users
ports:
- "3002:3000"
api-gateway:
build: ./api-gateway
ports:
- "8000:8000"
Démarrer les services avec Docker Compose :
docker-compose up --build
Erreurs frequentes et debugging
1. Erreur de connexion à la base de données
Code incorrect :
mongoose.connect('mongodb://localhost/tasksdb', {
useNewUrlParser: true,
useUnifiedTopology: true,
}, (err) => {
if (err) throw err;
});
Code correct :
mongoose.connect(process.env.MONGO_URI, {
useNewUrlParser: true,
useUnifiedTopology: true,
}, (err) => {
if (err) throw err;
});
2. Erreur d'importation des modules
Code incorrect :
const express = require('express');
Code correct :
const express = require('express');
3. Erreur de port occupé
Code incorrect :
app.listen(3000, () => {
console.log('Serveur démarré sur le port 3000');
});
Code correct :
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Serveur démarré sur le port ${PORT}`);
});
Pour aller plus loin
1. Implémenter des tests unitaires et d'intégration
Utilisez jest pour écrire des tests unitaires et d'intégration pour votre application microservices.
Installation :
npm install jest --save-dev
Exemple de test unitaire :
const request = require('supertest');
const app = require('./server');
describe('GET /tasks', () => {
it('should return a list of tasks', async () => {
const response = await request(app).get('/tasks');
expect(response.status).toBe(200);
expect(Array.isArray(response.body)).toBe(true);
});
});
2. Utiliser Kubernetes pour gérer les déploiements
Kubernetes est une plateforme de gestion d'orchestration de conteneurs qui peut être utilisée pour déployer et gérer les microservices.
Installation :
## Installer Minikube sur votre machine locale
curl -LO https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64
sudo install minikube-linux-amd64 /usr/local/bin/minikube
## Démarrer Minikube
minikube start
Déploiement avec Kubernetes :
kubectl apply -f tasks-deployment.yaml
kubectl apply -f users-deployment.yaml
kubectl apply -f api-gateway-deployment.yaml
3. Sécuriser les microservices
Utilisez des techniques de sécurité comme l'authentification JWT, la gestion des autorisations et le chiffrement des données pour sécuriser vos microservices.
Exemple d'authentification JWT :
npm install jsonwebtoken
javascript
const jwt = require('jsonwebtoken');
app.post('/login', (req, res) => {
const user = { id: 1 };
const token = jwt.sign(user, 'secret_key');
res.json({ token });
});
Défi pratique : Ajouter une fonctionnalité d'authentification
Ajoutez une fonctionnalité d'authentification JWT à votre application microservices. Créez un service d'authentification qui génère un token JWT et vérifie les tokens dans les requêtes des autres services.
Conclusion
Le développement avec Express en utilisant l'architecture microservices offre de nombreuses avantages pour construire des applications modernes et évolutives. En suivant ce tutoriel, vous devriez être capable de créer, déployer et gérer vos propres microservices avec succès. N'oubliez pas que la sécurité, les tests unitaires et d'intégration sont des éléments cruciaux à considérer lors du développement de microservices.