📚 Ressources — Node.js & Express
Cheatsheet complète de la formation — toutes les APIs, patterns et commandes essentiels en un coup d'œil.
⚙️ Node.js Core — Objets globaux
| Variable | Description |
| process | Infos processus, env vars, exit |
| process.env.X | Lire une variable d'environnement |
| process.argv | Arguments de la ligne de commande |
| process.exit(1) | Arrêter le processus avec code |
| __dirname | Répertoire du fichier courant (absolu) |
| __filename | Chemin complet du fichier courant |
| require('mod') | Importer un module |
| module.exports | Exporter depuis un module |
📦 Modules natifs
| Module / Méthode | Usage |
| fs.readFileSync(p, 'utf-8') | Lire un fichier (synchrone) |
| fs.writeFileSync(p, data) | Écrire un fichier (synchrone) |
| fs.existsSync(p) | Vérifier qu'un fichier existe |
| path.join(__dirname, f) | Chemin absolu robuste |
| path.resolve(rel) | Résoudre un chemin relatif |
| path.extname('f.js') | → '.js' |
| http.createServer(cb) | Serveur HTTP bas niveau |
| crypto.randomBytes(n) | Bytes aléatoires sécurisés |
| os.cpus().length | Nombre de CPUs disponibles |
📥 npm essentiels
| Commande | Description |
| npm init -y | Créer package.json avec défauts |
| npm install pkg | Installer une dépendance |
| npm install -D pkg | Dépendance dev uniquement |
| npm install | Installer depuis package.json |
| npm run start | Lancer le script "start" |
| npm run dev | Lancer le script "dev" |
| npx pkg | Exécuter sans installer globalement |
| ^4.18.2 | Semver : compatible 4.x.x |
| ~4.18.2 | Semver : compatible 4.18.x |
🚀 Express — Setup & Routes
| Code | Description |
| const app = express() | Créer l'application |
| app.use(express.json()) | Parser le body JSON |
| app.get(path, handler) | Route GET |
| app.post(path, handler) | Route POST |
| app.put(path, handler) | Route PUT (remplacement) |
| app.patch(path, handler) | Route PATCH (partiel) |
| app.delete(path, handler) | Route DELETE |
| app.use(middleware) | Middleware global |
| app.listen(port, cb) | Démarrer le serveur |
📩 Objet req
| Propriété | Contenu |
| req.params.id | Paramètre d'URL (:id) |
| req.query.page | Query string (?page=2) |
| req.body.titre | Corps de la requête (JSON) |
| req.headers['authorization'] | En-tête HTTP spécifique |
| req.method | 'GET', 'POST', etc. |
| req.path | '/api/users' |
| req.ip | IP du client |
| req.user | Défini par un middleware auth |
📤 Objet res
| Méthode | Usage |
| res.json(obj) | Répondre en JSON (Content-Type auto) |
| res.status(404) | Définir le status code (chaînable) |
| res.status(201).json(obj) | Status + JSON en une ligne |
| res.send('texte') | Répondre en texte brut |
| res.status(204).send() | No Content (DELETE réussi) |
| res.redirect('/login') | Redirection 302 |
| res.setHeader('X', 'v') | Définir un en-tête HTTP |
🔧 Pattern Middleware
// Middleware standard (3 params)
function logger(req, res, next) {
console.log(req.method, req.path);
next(); // NE PAS oublier !
}
// Monter globalement
app.use(logger);
// Error handler (4 params OBLIGATOIRES)
app.use((err, req, res, next) => {
res.status(err.status || 500)
.json({ error: err.message });
});
// next(err) → propage l'erreur
app.get('/test', (req, res, next) => {
next(new Error('Oups'));
});
🗺️ Router Express
// routes/articles.js
const router = express.Router();
// Chaînage sur même URL
router.route('/')
.get((req, res) => { /*...*/ })
.post((req, res) => { /*...*/ });
router.route('/:id')
.get((req, res) => { /*...*/ })
.put((req, res) => { /*...*/ })
.delete((req, res) => { /*...*/ });
// Middleware de paramètre
router.param('id', (req, res, next, id) => {
if (isNaN(+id)) return res.status(400).json({error:'ID invalide'});
next();
});
module.exports = router;
// server.js
app.use('/articles', require('./routes/articles'));
🔐 JWT — jsonwebtoken
const jwt = require('jsonwebtoken');
const SECRET = process.env.JWT_SECRET;
// Créer un token
const token = jwt.sign(
{ id: user.id, role: user.role }, // payload
SECRET,
{ expiresIn: '7d' }
);
// Vérifier un token
try {
const payload = jwt.verify(token, SECRET);
// payload.id, payload.role, payload.exp
} catch (err) {
// TokenExpiredError ou JsonWebTokenError
}
// Middleware verifyToken
function verifyToken(req, res, next) {
const header = req.headers.authorization;
if (!header?.startsWith('Bearer '))
return res.status(401).json({ error: 'Token manquant' });
try {
req.user = jwt.verify(header.slice(7), SECRET);
next();
} catch {
res.status(401).json({ error: 'Token invalide' });
}
}
// Header HTTP : Authorization: Bearer eyJhbG...
🔒 bcrypt — Hachage de mots de passe
const bcrypt = require('bcryptjs');
// Hacher (async — à faire au register)
const hash = await bcrypt.hash(password, 10);
// 10 = salt rounds (coût de calcul)
// Stocker `hash` en base, jamais `password`
// Vérifier (async — à faire au login)
const match = await bcrypt.compare(plainText, hash);
if (!match) return res.status(401).json({ error: 'Identifiants invalides' });
// NE JAMAIS :
// ✗ stocker des mots de passe en clair
// ✗ utiliser MD5/SHA1 pour les mots de passe
// ✗ comparer avec ===
📊 Status HTTP courants
| Code | Signification | Quand ? |
| 200 | OK | GET/PUT/PATCH réussi |
| 201 | Created | POST réussi (ressource créée) |
| 204 | No Content | DELETE réussi (corps vide) |
| 400 | Bad Request | Champs requis manquants |
| 401 | Unauthorized | Non authentifié (token absent/invalide) |
| 403 | Forbidden | Authentifié mais pas autorisé |
| 404 | Not Found | Ressource introuvable |
| 409 | Conflict | Email déjà utilisé, doublon |
| 422 | Unprocessable | Données sémantiquement invalides |
| 500 | Server Error | Erreur interne imprévue |
🛡️ Sécurité production
require('dotenv').config();
const helmet = require('helmet');
const cors = require('cors');
const rateLimit = require('express-rate-limit');
app.use(helmet()); // En-têtes HTTP sécurisés
app.use(cors({ origin: [...] })); // Whitelist origines
app.use(rateLimit({ // Anti-DDoS
windowMs: 15 * 60 * 1000,
max: 100
}));
app.use(express.json({ limit: '10kb' }));
// asyncHandler — plus de try/catch répétitifs
const asyncHandler = fn => (req, res, next) =>
Promise.resolve(fn(req, res, next)).catch(next);
// Error handler production
app.use((err, req, res, next) => {
const isDev = process.env.NODE_ENV !== 'production';
res.status(err.status || 500).json({
error: isDev ? err.message : 'Erreur serveur'
});
});
🗄️ Pattern JsonDB — Persistance fichier
const fs = require('fs'), path = require('path');
class JsonDB {
constructor(filename) {
this.filepath = path.join(__dirname, filename);
this.data = this._load();
}
_load() {
try { return JSON.parse(fs.readFileSync(this.filepath, 'utf-8')); }
catch { return {}; }
}
_save() { fs.writeFileSync(this.filepath, JSON.stringify(this.data, null, 2)); }
getAll(key) { return this.data[key] || []; }
getById(key, id) { return this.getAll(key).find(i => i.id === parseInt(id)); }
insert(key, item) {
const all = this.getAll(key);
item.id = all.length ? Math.max(...all.map(i => i.id)) + 1 : 1;
item.createdAt = new Date().toISOString();
all.push(item); this.data[key] = all; this._save(); return item;
}
update(key, id, data) {
const all = this.getAll(key);
const idx = all.findIndex(i => i.id === parseInt(id));
if (idx === -1) return null;
all[idx] = { ...all[idx], ...data, id: parseInt(id) };
this.data[key] = all; this._save(); return all[idx];
}
delete(key, id) {
const all = this.getAll(key);
const next = all.filter(i => i.id !== parseInt(id));
if (next.length === all.length) return false;
this.data[key] = next; this._save(); return true;
}
}
module.exports = JsonDB;
✅ Bonnes pratiques Node.js & Express
Async / Erreurs
- Toujours async/await plutôt que callbacks imbriqués
- Entourer les I/O de try/catch
- Utiliser asyncHandler pour chaque route async
- Error handler 4-params en dernier middleware
Validation
- Valider avant d'écrire en base
- return res.status(400) stoppe l'exécution
- Regex pour les emails : /^[^\s@]+@[^\s@]+\.[^\s@]+$/
- Extraire la validation dans un middleware
Sécurité
- Ne jamais exposer le stack trace en production
- Toujours hacher les mots de passe avec bcrypt
- Utiliser process.env pour tous les secrets
- Commiter
.env.example, jamais .env
REST API
- Status 201 pour POST, 204 pour DELETE réussi
- Format uniforme { success, data }
- Toujours paginer les listes (?page=&limit=)
- Noms de routes au pluriel :
/articles pas /article