Node.js + MongoDB : créer une API scalable

🏷️ Back-end 📅 27/01/2026 11:00:00 👤 Mezgani said
Nodejs Mongodb Api Database Orm
Node.js + MongoDB : créer une API scalable

Construisez une API production-ready avec Node.js et MongoDB : connexion Mongoose, modèles, validation, agrégations et bonnes pratiques.

Configuration Mongoose

Mongoose est un ODM (Object Document Mapper) pour MongoDB qui facilite la gestion des données. Commencez par installer les dépendances :

npm install mongoose dotenv
npm install -D nodemon

Créez un fichier de configuration pour gérer la connexion à MongoDB :

// config/database.js
const mongoose = require('mongoose');
require('dotenv').config();

const connectDB = async () => {
    try {
        await mongoose.connect(process.env.MONGODB_URI, {
            useNewUrlParser: true,
            useUnifiedTopology: true
        });
        console.log('✓ MongoDB connecté');
    } catch (error) {
        console.error('✗ Erreur connexion:', error.message);
        process.exit(1);
    }
};

module.exports = connectDB;
À retenir : Utilisez toujours des variables d'environnement pour les connexions (fichier .env non commité).

Schémas et validation

Les schémas Mongoose définissent la structure des documents et incluent la validation native :

// models/User.js
const mongoose = require('mongoose');

const userSchema = new mongoose.Schema({
    name: {
        type: String,
        required: [true, 'Le nom est obligatoire'],
        trim: true,
        minlength: [3, 'Minimum 3 caractères']
    },
    email: {
        type: String,
        required: true,
        unique: true,
        lowercase: true,
        match: [/^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/, 'Email invalide']
    },
    age: {
        type: Number,
        min: 18,
        max: 120
    },
    createdAt: {
        type: Date,
        default: Date.now
    }
});

module.exports = mongoose.model('User', userSchema);
Note : Les validateurs intégrés (required, min, max, match) s'exécutent avant la sauvegarde. Utilisez la méthode validate() pour valider manuellement.

Modèles et relations

Mongoose supporte les relations One-to-Many et Many-to-Many via les références :

// models/Post.js
const postSchema = new mongoose.Schema({
    title: String,
    content: String,
    author: {
        type: mongoose.Schema.Types.ObjectId,
        ref: 'User',
        required: true
    },
    tags: [{
        type: String,
        enum: ['javascript', 'nodejs', 'mongodb']
    }],
    likes: [{
        type: mongoose.Schema.Types.ObjectId,
        ref: 'User'
    }]
});

module.exports = mongoose.model('Post', postSchema);

Pour récupérer les données liées, utilisez populate() :

// routes/posts.js
const posts = await Post.find()
    .populate('author', 'name email')
    .populate('likes', 'name')
    .lean();

// .lean() retourne un objet JavaScript simple (plus rapide)

Optimisation des requêtes

Les requêtes non optimisées causent des ralentissements. Voici les meilleures pratiques :

ProblèmeSolution
N+1 queriesUtiliser populate() ou aggregation()
Champs inutilesSélectionner uniquement les champs nécessaires
Pas d'indexCréer des index sur les champs fréquemment interrogés
Pas de paginationUtiliser limit() et skip()
// ❌ MAUVAIS - Charge TOUS les utilisateurs
const posts = await Post.find();
for (let post of posts) {
    const user = await User.findById(post.author); // N+1 queries!
}

// ✅ BON - Une seule requête
const posts = await Post.find()
    .populate('author')
    .select('title author');  // Sélectionner uniquement les champs nécessaires

// ✅ EXCELLENT - Avec pagination et indexation
const page = req.query.page || 1;
const limit = 20;
const posts = await Post.find()
    .populate('author')
    .skip((page - 1) * limit)
    .limit(limit)
    .lean();

Créez des index sur les champs interrogés fréquemment :

// models/Post.js
postSchema.index({ createdAt: -1 });
postSchema.index({ author: 1 });
postSchema.index({ title: 'text', content: 'text' }); // Full-text search

Gestion d'erreurs

Gérez les erreurs Mongoose de manière appropriée :

// middleware/errorHandler.js
const handleMongooseError = (error, res) => {
    // Erreur de validation
    if (error.name === 'ValidationError') {
        const messages = Object.values(error.errors)
            .map(err => err.message);
        return res.status(400).json({ errors: messages });
    }

    // Erreur de duplication (unique)
    if (error.code === 11000) {
        const field = Object.keys(error.keyPattern)[0];
        return res.status(400).json({
            error: `${field} doit être unique`
        });
    }

    // Cast error (ID invalide)
    if (error.name === 'CastError') {
        return res.status(400).json({ error: 'ID invalide' });
    }

    // Erreur serveur
    return res.status(500).json({ error: error.message });
};

// routes/users.js
router.post('/users', async (req, res) => {
    try {
        const user = new User(req.body);
        await user.save();
        res.status(201).json(user);
    } catch (error) {
        handleMongooseError(error, res);
    }
});
À retenir : Gérez toujours les erreurs de validation et de duplication pour une API robuste.