Back-end angularforall.com

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

Node-Js Mongodb Api Database Mongoose Rest-Api Javascript Backend Scalabilite Nosql Agregations Express Validation Best-Practices
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 de scalabilité.

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.

Partager