Credit : Illustration backtotheweb.fr
Creer une API REST avec Node.js et Express
Initialiser le projet
Creez un projet Node.js avec la structure suivante :
mkdir mon-api && cd mon-api
npm init -y
npm install express mongoose jsonwebtoken bcryptjs joi dotenv cors helmet
npm install -D nodemon
Ajoutez le script de developpement dans package.json :
"scripts": {
"dev": "nodemon src/index.js",
"start": "node src/index.js"
}
Point d'entree et middleware
Creez src/index.js :
const express = require('express');
const mongoose = require('mongoose');
const cors = require('cors');
const helmet = require('helmet');
require('dotenv').config();
const app = express();
// Middleware globaux
app.use(helmet()); // Headers de securite
app.use(cors()); // Cross-Origin
app.use(express.json()); // Parser JSON
app.use(express.urlencoded({ extended: true }));
// Connexion MongoDB
mongoose.connect(process.env.MONGO_URI)
.then(() => console.log('MongoDB connecte'))
.catch(err => console.error('Erreur MongoDB:', err));
// Routes
app.use('/api/auth', require('./routes/auth'));
app.use('/api/articles', require('./routes/articles'));
// Gestion d'erreurs globale
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(err.status || 500).json({
error: err.message || 'Erreur interne du serveur'
});
});
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => console.log(`API demarree sur le port ${PORT}`));
Modele Mongoose
Creez src/models/Article.js :
const mongoose = require('mongoose');
const articleSchema = new mongoose.Schema({
title: { type: String, required: true, trim: true },
content: { type: String, required: true },
author: { type: mongoose.Schema.Types.ObjectId, ref: 'User', required: true },
tags: [String],
status: { type: String, enum: ['draft', 'published'], default: 'draft' }
}, { timestamps: true });
articleSchema.index({ title: 'text', content: 'text' });
module.exports = mongoose.model('Article', articleSchema);
Validation avec Joi
Creez src/validators/article.js :
const Joi = require('joi');
const articleValidator = (req, res, next) => {
const schema = Joi.object({
title: Joi.string().min(3).max(200).required(),
content: Joi.string().min(10).required(),
tags: Joi.array().items(Joi.string().max(30)).max(10),
status: Joi.string().valid('draft', 'published')
});
const { error } = schema.validate(req.body);
if (error) {
return res.status(400).json({ error: error.details[0].message });
}
next();
};
module.exports = articleValidator;
Authentification JWT
Creez src/middleware/auth.js :
Astuce : j'ai appris ça à la dure après un incident en prod un vendredi soir.
const jwt = require('jsonwebtoken');
const auth = (req, res, next) => {
const header = req.header('Authorization');
if (!header || !header.startsWith('Bearer ')) {
return res.status(401).json({ error: 'Token manquant' });
}
try {
const token = header.replace('Bearer ', '');
const decoded = jwt.verify(token, process.env.JWT_SECRET);
req.user = decoded;
next();
} catch (err) {
res.status(401).json({ error: 'Token invalide' });
}
};
module.exports = auth;
Routes CRUD
Creez src/routes/articles.js :
const router = require('express').Router();
const Article = require('../models/Article');
const auth = require('../middleware/auth');
const validate = require('../validators/article');
// GET /api/articles - Liste avec pagination
router.get('/', async (req, res) => {
const page = parseInt(req.query.page) || 1;
const limit = parseInt(req.query.limit) || 10;
const skip = (page - 1) * limit;
const [articles, total] = await Promise.all([
Article.find({ status: 'published' })
.populate('author', 'name')
.sort({ createdAt: -1 })
.skip(skip)
.limit(limit),
Article.countDocuments({ status: 'published' })
]);
res.json({ articles, page, totalPages: Math.ceil(total / limit), total });
});
// GET /api/articles/:id
router.get('/:id', async (req, res) => {
const article = await Article.findById(req.params.id).populate('author', 'name');
if (!article) return res.status(404).json({ error: 'Article non trouve' });
res.json(article);
});
// POST /api/articles - Creer (authentifie)
router.post('/', auth, validate, async (req, res) => {
const article = new Article({ ...req.body, author: req.user.id });
await article.save();
res.status(201).json(article);
});
// PUT /api/articles/:id - Modifier
router.put('/:id', auth, validate, async (req, res) => {
const article = await Article.findOneAndUpdate(
{ _id: req.params.id, author: req.user.id },
req.body,
{ new: true, runValidators: true }
);
if (!article) return res.status(404).json({ error: 'Article non trouve' });
res.json(article);
});
// DELETE /api/articles/:id
router.delete('/:id', auth, async (req, res) => {
const article = await Article.findOneAndDelete({ _id: req.params.id, author: req.user.id });
if (!article) return res.status(404).json({ error: 'Article non trouve' });
res.json({ message: 'Article supprime' });
});
module.exports = router;
Deployer sur votre serveur IONOS
Utilisez PM2 pour gerer le processus :
npm install -g pm2
pm2 start src/index.js --name mon-api
pm2 save
pm2 startup
Votre API REST est prete, avec authentification JWT, validation des donnees et pagination.