Entrez vos informations pour commencer votre parcours d'apprentissage n8n. Votre certificat sera personnalisé avec votre nom.
Cours interactifs · Exercices pratiques · Quiz intégrés
Les syntaxes essentielles en un coup d'œil
Testez vos connaissances sur n8n
Maîtrisez Claude et ExplorIA pour révolutionner votre productivité. Guide complet avec exemples pratiques et cas d'usage avancés.
Apprenez les bases de l'IA conversationnelle avec Claude. Techniques de prompting avancées et optimisation des réponses.
Intégrez ExplorIA dans vos workflows. Automatisation intelligente et augmentation de votre productivité.
Exemples concrets d'utilisation en entreprise. De la génération de code à l'analyse de données.
Téléchargez nos guides détaillés avec exercices pratiques et templates prêts à l'emploi.
Visualisez directement le premier guide dans votre navigateur :
Créez des API RESTful modernes avec Node.js, Express et les meilleures pratiques du développement backend.
Niveau: Intermédiaire
Prérequis: Connaissances de base en JavaScript
Objectif: Créer des API RESTful complètes et sécurisées
Une API REST (Representational State Transfer) est une interface de programmation qui permet à des applications de communiquer entre elles via HTTP. Elle suit des principes architecturaux spécifiques :
# Vérifier si Node.js est installé node --version npm --version # Si pas installé, télécharger depuis https://nodejs.org # Ou utiliser nvm (recommandé) curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash source ~/.bashrc nvm install --lts nvm use --lts
# Créer le dossier du projet mkdir api-taches cd api-taches # Initialiser le projet Node.js npm init -y # Installer les dépendances npm install express mongoose dotenv bcryptjs jsonwebtoken cors helmet morgan npm install --save-dev nodemon jest supertest
api-taches/ ├── src/ │ ├── controllers/ │ │ └── taskController.js │ ├── models/ │ │ └── Task.js │ ├── routes/ │ │ └── taskRoutes.js │ ├── middleware/ │ │ ├── auth.js │ │ └── errorHandler.js │ └── app.js ├── tests/ │ └── task.test.js ├── .env ├── package.json └── server.js
const app = require('./src/app');
const mongoose = require('mongoose');
require('dotenv').config();
const PORT = process.env.PORT || 3000;
// Connexion à MongoDB
mongoose.connect(process.env.MONGODB_URI, {
useNewUrlParser: true,
useUnifiedTopology: true,
})
.then(() => console.log('✅ Connecté à MongoDB'))
.catch(err => console.error('❌ Erreur MongoDB:', err));
// Démarrage du serveur
app.listen(PORT, () => {
console.log(`🚀 Serveur démarré sur le port ${PORT}`);
console.log(`📱 API disponible sur http://localhost:${PORT}`);
});
const express = require('express');
const cors = require('cors');
const helmet = require('helmet');
const morgan = require('morgan');
const taskRoutes = require('./routes/taskRoutes');
const errorHandler = require('./middleware/errorHandler');
const app = express();
// Middleware de sécurité
app.use(helmet());
app.use(cors());
// Middleware de logging
app.use(morgan('combined'));
// Middleware pour parser le JSON
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
// Routes
app.use('/api/tasks', taskRoutes);
// Route de santé
app.get('/health', (req, res) => {
res.json({ status: 'OK', timestamp: new Date().toISOString() });
});
// Middleware de gestion d'erreurs
app.use(errorHandler);
// Route 404
app.use('*', (req, res) => {
res.status(404).json({ error: 'Route non trouvée' });
});
module.exports = app;
const mongoose = require('mongoose');
const taskSchema = new mongoose.Schema({
title: {
type: String,
required: [true, 'Le titre est obligatoire'],
trim: true,
maxlength: [100, 'Le titre ne peut pas dépasser 100 caractères']
},
description: {
type: String,
trim: true,
maxlength: [500, 'La description ne peut pas dépasser 500 caractères']
},
completed: {
type: Boolean,
default: false
},
priority: {
type: String,
enum: ['low', 'medium', 'high'],
default: 'medium'
},
user: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
required: true
}
}, {
timestamps: true
});
// Index pour optimiser les recherches
taskSchema.index({ user: 1, createdAt: -1 });
taskSchema.index({ completed: 1, priority: 1 });
module.exports = mongoose.model('Task', taskSchema);
const Task = require('../models/Task');
// @desc Récupérer toutes les tâches de l'utilisateur
// @route GET /api/tasks
// @access Private
const getTasks = async (req, res) => {
try {
const tasks = await Task.find({ user: req.user.id }).sort('-createdAt');
res.json(tasks);
} catch (error) {
res.status(500).json({ error: 'Erreur serveur' });
}
};
// @desc Récupérer une tâche spécifique
// @route GET /api/tasks/:id
// @access Private
const getTask = async (req, res) => {
try {
const task = await Task.findById(req.params.id);
if (!task) {
return res.status(404).json({ error: 'Tâche non trouvée' });
}
// Vérifier que l'utilisateur possède cette tâche
if (task.user.toString() !== req.user.id) {
return res.status(401).json({ error: 'Accès non autorisé' });
}
res.json(task);
} catch (error) {
res.status(500).json({ error: 'Erreur serveur' });
}
};
// @desc Créer une nouvelle tâche
// @route POST /api/tasks
// @access Private
const createTask = async (req, res) => {
const { title, description, priority } = req.body;
if (!title) {
return res.status(400).json({ error: 'Le titre est obligatoire' });
}
try {
const task = await Task.create({
title,
description,
priority: priority || 'medium',
user: req.user.id
});
res.status(201).json(task);
} catch (error) {
res.status(500).json({ error: 'Erreur serveur' });
}
};
// @desc Mettre à jour une tâche
// @route PUT /api/tasks/:id
// @access Private
const updateTask = async (req, res) => {
try {
const task = await Task.findById(req.params.id);
if (!task) {
return res.status(404).json({ error: 'Tâche non trouvée' });
}
// Vérifier que l'utilisateur possède cette tâche
if (task.user.toString() !== req.user.id) {
return res.status(401).json({ error: 'Accès non autorisé' });
}
const updatedTask = await Task.findByIdAndUpdate(
req.params.id,
req.body,
{ new: true, runValidators: true }
);
res.json(updatedTask);
} catch (error) {
res.status(500).json({ error: 'Erreur serveur' });
}
};
// @desc Supprimer une tâche
// @route DELETE /api/tasks/:id
// @access Private
const deleteTask = async (req, res) => {
try {
const task = await Task.findById(req.params.id);
if (!task) {
return res.status(404).json({ error: 'Tâche non trouvée' });
}
// Vérifier que l'utilisateur possède cette tâche
if (task.user.toString() !== req.user.id) {
return res.status(401).json({ error: 'Accès non autorisé' });
}
await task.remove();
res.json({ message: 'Tâche supprimée' });
} catch (error) {
res.status(500).json({ error: 'Erreur serveur' });
}
};
module.exports = {
getTasks,
getTask,
createTask,
updateTask,
deleteTask
};
const express = require('express');
const router = express.Router();
const {
getTasks,
getTask,
createTask,
updateTask,
deleteTask
} = require('../controllers/taskController');
const { protect } = require('../middleware/auth');
// Toutes les routes nécessitent une authentification
router.use(protect);
// Routes CRUD
router.route('/')
.get(getTasks)
.post(createTask);
router.route('/:id')
.get(getTask)
.put(updateTask)
.delete(deleteTask);
module.exports = router;
const jwt = require('jsonwebtoken');
const User = require('../models/User');
// Middleware pour protéger les routes
const protect = async (req, res, next) => {
let token;
// Vérifier si le token est présent dans les headers
if (req.headers.authorization && req.headers.authorization.startsWith('Bearer')) {
try {
// Extraire le token
token = req.headers.authorization.split(' ')[1];
// Vérifier le token
const decoded = jwt.verify(token, process.env.JWT_SECRET);
// Récupérer l'utilisateur (sans le mot de passe)
req.user = await User.findById(decoded.id).select('-password');
next();
} catch (error) {
res.status(401).json({ error: 'Token invalide' });
}
}
if (!token) {
res.status(401).json({ error: 'Accès non autorisé, token manquant' });
}
};
// Middleware pour vérifier les rôles
const authorize = (...roles) => {
return (req, res, next) => {
if (!roles.includes(req.user.role)) {
return res.status(403).json({
error: `Le rôle ${req.user.role} n'est pas autorisé à accéder à cette ressource`
});
}
next();
};
};
module.exports = { protect, authorize };
const errorHandler = (err, req, res, next) => {
let error = { ...err };
error.message = err.message;
// Log de l'erreur
console.error(err);
// Erreur de validation Mongoose
if (err.name === 'ValidationError') {
const message = Object.values(err.errors).map(val => val.message);
error = { message, statusCode: 400 };
}
// Erreur de clé dupliquée Mongoose
if (err.code === 11000) {
const field = Object.keys(err.keyValue)[0];
const message = `Valeur dupliquée pour le champ: ${field}`;
error = { message, statusCode: 400 };
}
// Erreur de cast Mongoose (ID invalide)
if (err.name === 'CastError') {
const message = 'Ressource non trouvée';
error = { message, statusCode: 404 };
}
// Erreur JWT
if (err.name === 'JsonWebTokenError') {
const message = 'Token invalide';
error = { message, statusCode: 401 };
}
res.status(error.statusCode || 500).json({
success: false,
error: error.message || 'Erreur serveur',
...(process.env.NODE_ENV === 'development' && { stack: err.stack })
});
};
module.exports = errorHandler;
const mongoose = require('mongoose');
const bcrypt = require('bcryptjs');
const jwt = require('jsonwebtoken');
const userSchema = new mongoose.Schema({
name: {
type: String,
required: [true, 'Le nom est obligatoire'],
trim: true,
maxlength: [50, 'Le nom ne peut pas dépasser 50 caractères']
},
email: {
type: String,
required: [true, 'L\'email est obligatoire'],
unique: true,
lowercase: true,
match: [
/^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/,
'Veuillez entrer un email valide'
]
},
password: {
type: String,
required: [true, 'Le mot de passe est obligatoire'],
minlength: [6, 'Le mot de passe doit contenir au moins 6 caractères'],
select: false // Ne pas inclure dans les requêtes par défaut
},
role: {
type: String,
enum: ['user', 'admin'],
default: 'user'
}
}, {
timestamps: true
});
// Hash du mot de passe avant sauvegarde
userSchema.pre('save', async function(next) {
if (!this.isModified('password')) {
next();
}
const salt = await bcrypt.genSalt(10);
this.password = await bcrypt.hash(this.password, salt);
});
// Méthode pour vérifier le mot de passe
userSchema.methods.matchPassword = async function(enteredPassword) {
return await bcrypt.compare(enteredPassword, this.password);
};
// Méthode pour générer le JWT
userSchema.methods.getSignedJwtToken = function() {
return jwt.sign({ id: this._id }, process.env.JWT_SECRET, {
expiresIn: process.env.JWT_EXPIRE
});
};
module.exports = mongoose.model('User', userSchema);
const User = require('../models/User');
// @desc Inscription utilisateur
// @route POST /api/auth/register
// @access Public
const register = async (req, res) => {
const { name, email, password } = req.body;
try {
// Vérifier si l'utilisateur existe déjà
const userExists = await User.findOne({ email });
if (userExists) {
return res.status(400).json({ error: 'Utilisateur déjà existant' });
}
// Créer l'utilisateur
const user = await User.create({
name,
email,
password
});
// Générer le token
const token = user.getSignedJwtToken();
res.status(201).json({
success: true,
token,
user: {
id: user._id,
name: user.name,
email: user.email,
role: user.role
}
});
} catch (error) {
res.status(500).json({ error: 'Erreur serveur' });
}
};
// @desc Connexion utilisateur
// @route POST /api/auth/login
// @access Public
const login = async (req, res) => {
const { email, password } = req.body;
try {
// Vérifier l'email et inclure le mot de passe
const user = await User.findOne({ email }).select('+password');
if (!user) {
return res.status(401).json({ error: 'Identifiants invalides' });
}
// Vérifier le mot de passe
const isMatch = await user.matchPassword(password);
if (!isMatch) {
return res.status(401).json({ error: 'Identifiants invalides' });
}
// Générer le token
const token = user.getSignedJwtToken();
res.json({
success: true,
token,
user: {
id: user._id,
name: user.name,
email: user.email,
role: user.role
}
});
} catch (error) {
res.status(500).json({ error: 'Erreur serveur' });
}
};
// @desc Récupérer l'utilisateur connecté
// @route GET /api/auth/me
// @access Private
const getMe = async (req, res) => {
const user = await User.findById(req.user.id);
res.json({
success: true,
user: {
id: user._id,
name: user.name,
email: user.email,
role: user.role
}
});
};
module.exports = {
register,
login,
getMe
};
// config/database.js
const mongoose = require('mongoose');
const connectDB = async () => {
try {
const conn = await mongoose.connect(process.env.MONGODB_URI, {
useNewUrlParser: true,
useUnifiedTopology: true,
useCreateIndex: true,
useFindAndModify: false
});
console.log(`✅ MongoDB connecté: ${conn.connection.host}`);
} catch (error) {
console.error('❌ Erreur de connexion MongoDB:', error.message);
process.exit(1);
}
};
module.exports = connectDB;
NODE_ENV=development PORT=3000 # Base de données MONGODB_URI=mongodb://localhost:27017/api-taches # JWT JWT_SECRET=votre_clé_secrète_très_longue_et_complexe_ici JWT_EXPIRE=30d # Autres configurations BCRYPT_ROUNDS=10
{
"name": "api-taches",
"version": "1.0.0",
"description": "API REST pour gestion de tâches",
"main": "server.js",
"scripts": {
"start": "node server.js",
"dev": "nodemon server.js",
"test": "jest",
"test:watch": "jest --watch"
},
"keywords": ["api", "rest", "express", "nodejs", "mongodb"],
"author": "Votre nom",
"license": "MIT"
}
const request = require('supertest');
const app = require('../src/app');
const User = require('../src/models/User');
const Task = require('../src/models/Task');
const mongoose = require('mongoose');
describe('API Tasks', () => {
let token;
let userId;
beforeAll(async () => {
// Connexion à la base de test
await mongoose.connect(process.env.MONGODB_TEST_URI);
// Créer un utilisateur de test
const user = await User.create({
name: 'Test User',
email: 'test@example.com',
password: 'password123'
});
userId = user._id;
// Générer un token
token = user.getSignedJwtToken();
});
afterAll(async () => {
// Nettoyer la base de données
await User.deleteMany();
await Task.deleteMany();
await mongoose.connection.close();
});
describe('GET /api/tasks', () => {
it('devrait récupérer toutes les tâches de l\'utilisateur', async () => {
const res = await request(app)
.get('/api/tasks')
.set('Authorization', `Bearer ${token}`)
.expect(200);
expect(Array.isArray(res.body)).toBe(true);
});
});
describe('POST /api/tasks', () => {
it('devrait créer une nouvelle tâche', async () => {
const taskData = {
title: 'Tâche de test',
description: 'Description de test',
priority: 'high'
};
const res = await request(app)
.post('/api/tasks')
.set('Authorization', `Bearer ${token}`)
.send(taskData)
.expect(201);
expect(res.body.title).toBe(taskData.title);
expect(res.body.user).toBe(userId.toString());
});
it('devrait échouer si le titre est manquant', async () => {
const res = await request(app)
.post('/api/tasks')
.set('Authorization', `Bearer ${token}`)
.send({})
.expect(400);
expect(res.body.error).toBe('Le titre est obligatoire');
});
});
});
# Installer Heroku CLI npm install -g heroku # Se connecter heroku login # Créer l'application heroku create nom-de-votre-api # Configurer les variables d'environnement heroku config:set NODE_ENV=production heroku config:set JWT_SECRET=votre_clé_secrète heroku config:set MONGODB_URI=votre_uri_mongodb_atlas # Déployer git push heroku main # Ouvrir l'application heroku open
# Utiliser l'image officielle Node.js FROM node:16-alpine # Définir le répertoire de travail WORKDIR /app # Copier package.json et package-lock.json COPY package*.json ./ # Installer les dépendances RUN npm ci --only=production # Copier le code source COPY . . # Exposer le port EXPOSE 3000 # Commande de démarrage CMD ["npm", "start"]
Complétez toutes les leçons pour débloquer et télécharger votre certificat officiel.