NestJS en MongoDB in 2026: Mongoose, Aggregaties en Sollicitatievragen
Praktische handleiding voor NestJS 12 met MongoDB en Mongoose 9: schema-ontwerp, aggregation pipelines, transacties en technische sollicitatievragen voor backend-ontwikkelaars.

NestJS 12 gecombineerd met MongoDB via Mongoose 9 biedt een productie-klare stack voor het bouwen van schaalbare Node.js backends. Deze handleiding behandelt schema-ontwerppatronen, aggregation pipelines en de sollicitatievragen die senior kandidaten onderscheiden van junioren.
Mongoose 9.9.4 vereist Node.js 18+ en ondersteunt MongoDB 6.0 tot en met 8.0. NestJS 12 levert ESM-ready packages maar blijft achterwaarts compatibel met CommonJS projecten.
Mongoose Instellen in een NestJS 12 Applicatie
Het @nestjs/mongoose package integreert Mongoose met NestJS dependency injection. De installatie van de benodigde dependencies gebeurt via npm:
# Install Mongoose integration
npm install @nestjs/mongoose mongooseDe verbinding wordt geregistreerd in de root module:
import { Module } from '@nestjs/common';
import { MongooseModule } from '@nestjs/mongoose';
@Module({
imports: [
MongooseModule.forRoot(process.env.MONGODB_URI, {
// Connection pool size for production workloads
maxPoolSize: 10,
// Timeout after 10 seconds if connection fails
serverSelectionTimeoutMS: 10000,
}),
],
})
export class AppModule {}De forRoot methode accepteert alle Mongoose verbindingsopties. Het instellen van maxPoolSize voorkomt connection-uitputting onder belasting, een veelvoorkomend productieprobleem.
Schema-ontwerp met TypeScript Decorators
Mongoose schemas in NestJS gebruiken decorators van @nestjs/mongoose. Elk schema wordt gekoppeld aan een MongoDB collection.
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
import { HydratedDocument, Types } from 'mongoose';
// Document type for TypeScript autocomplete
export type UserDocument = HydratedDocument<User>;
@Schema({
timestamps: true, // Adds createdAt and updatedAt
collection: 'users', // Explicit collection name
})
export class User {
// MongoDB ObjectId, auto-generated
_id: Types.ObjectId;
@Prop({ required: true, unique: true, index: true })
email: string;
@Prop({ required: true })
passwordHash: string;
@Prop({ type: String, enum: ['admin', 'user', 'guest'], default: 'user' })
role: string;
@Prop({ type: [String], default: [] })
permissions: string[];
}
export const UserSchema = SchemaFactory.createForClass(User);De @Prop decorator definieert veldbeperkingen. Het instellen van index: true op veelgebruikte velden verbetert de leesprestaties ten koste van tragere schrijfoperaties.
Interviewers vragen naar de trade-off tussen embedded documenten en referenties. Embedded documenten zijn geschikt voor data die samen wordt opgehaald (gebruikersprofiel + voorkeuren). Referenties passen bij data die onbeperkt groeit of onafhankelijke queries vereist (gebruiker + bestellingen).
Repository Pattern met Injectable Services
NestJS moedigt het scheiden van databaselogica in services aan. De @InjectModel decorator biedt toegang tot het Mongoose model.
import { Injectable, NotFoundException } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { Model, Types } from 'mongoose';
import { User, UserDocument } from './user.schema';
@Injectable()
export class UserService {
constructor(
@InjectModel(User.name) private userModel: Model<UserDocument>,
) {}
async findById(id: string): Promise<UserDocument> {
// Validate ObjectId format before querying
if (!Types.ObjectId.isValid(id)) {
throw new NotFoundException('Invalid user ID format');
}
const user = await this.userModel.findById(id).exec();
if (!user) {
throw new NotFoundException(`User ${id} not found`);
}
return user;
}
async findByEmail(email: string): Promise<UserDocument | null> {
// Case-insensitive email lookup
return this.userModel.findOne({
email: { $regex: new RegExp(`^${email}$`, 'i') }
}).exec();
}
async create(data: Partial<User>): Promise<UserDocument> {
const user = new this.userModel(data);
return user.save();
}
}Het aanroepen van .exec() retourneert een echte Promise in plaats van een Mongoose Query object. Dit is belangrijk voor correct async/await gedrag en error stack traces.
Aggregation Pipelines voor Complexe Queries
MongoDB aggregaties behandelen reporting, analytics en datatransformaties die SQL databases oplossen met joins en GROUP BY.
import { Injectable } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { Model, PipelineStage } from 'mongoose';
import { Order, OrderDocument } from './order.schema';
@Injectable()
export class AnalyticsService {
constructor(
@InjectModel(Order.name) private orderModel: Model<OrderDocument>,
) {}
async getRevenueByMonth(year: number): Promise<MonthlyRevenue[]> {
const pipeline: PipelineStage[] = [
// Stage 1: Filter orders by year
{
$match: {
createdAt: {
$gte: new Date(`${year}-01-01`),
$lt: new Date(`${year + 1}-01-01`),
},
status: 'completed',
},
},
// Stage 2: Group by month, sum revenue
{
$group: {
_id: { $month: '$createdAt' },
totalRevenue: { $sum: '$amount' },
orderCount: { $sum: 1 },
avgOrderValue: { $avg: '$amount' },
},
},
// Stage 3: Sort by month ascending
{ $sort: { _id: 1 } },
// Stage 4: Reshape output
{
$project: {
_id: 0,
month: '$_id',
totalRevenue: { $round: ['$totalRevenue', 2] },
orderCount: 1,
avgOrderValue: { $round: ['$avgOrderValue', 2] },
},
},
];
return this.orderModel.aggregate(pipeline).exec();
}
}Aggregation pipelines verwerken documenten sequentieel door stages. Elke stage transformeert de output voor de volgende. De $match stage filtert vroeg om het aantal documenten dat door volgende stages wordt verwerkt te verminderen.
Klaar om je Node.js / NestJS gesprekken te halen?
Oefen met onze interactieve simulatoren, flashcards en technische tests.
Transacties voor Multi-Document Operaties
MongoDB 4.0+ ondersteunt multi-document ACID transacties. Transacties worden gebruikt wanneer meerdere documenten atomisch moeten worden bijgewerkt.
import { Injectable, BadRequestException } from '@nestjs/common';
import { InjectConnection, InjectModel } from '@nestjs/mongoose';
import { Connection, Model, ClientSession } from 'mongoose';
import { Account, AccountDocument } from './account.schema';
@Injectable()
export class TransferService {
constructor(
@InjectConnection() private connection: Connection,
@InjectModel(Account.name) private accountModel: Model<AccountDocument>,
) {}
async transfer(
fromId: string,
toId: string,
amount: number,
): Promise<void> {
// Start a session for the transaction
const session: ClientSession = await this.connection.startSession();
try {
await session.withTransaction(async () => {
// Debit source account
const source = await this.accountModel.findOneAndUpdate(
{ _id: fromId, balance: { $gte: amount } },
{ $inc: { balance: -amount } },
{ session, new: true },
);
if (!source) {
throw new BadRequestException('Insufficient balance or account not found');
}
// Credit destination account
const dest = await this.accountModel.findByIdAndUpdate(
toId,
{ $inc: { balance: amount } },
{ session, new: true },
);
if (!dest) {
throw new BadRequestException('Destination account not found');
}
});
} finally {
await session.endSession();
}
}
}De session.withTransaction wrapper handelt commit en rollback automatisch af. Als een operatie een fout gooit, wordt de hele transactie afgebroken.
Transacties vereisen een MongoDB replica set of sharded cluster. Standalone MongoDB instanties ondersteunen geen transacties. Atlas M0/M2/M5 tiers bevatten standaard replica sets.
Indexeringsstrategieën voor Query Performance
Indexen bepalen de query-prestaties. Zonder juiste indexen scant MongoDB volledige collections.
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
import { HydratedDocument } from 'mongoose';
export type ProductDocument = HydratedDocument<Product>;
@Schema({ timestamps: true })
export class Product {
@Prop({ required: true, index: true })
sku: string;
@Prop({ required: true })
name: string;
@Prop({ required: true })
category: string;
@Prop({ required: true })
price: number;
@Prop({ default: true })
inStock: boolean;
}
export const ProductSchema = SchemaFactory.createForClass(Product);
// Compound index for common query pattern
ProductSchema.index({ category: 1, price: -1 });
// Text index for search functionality
ProductSchema.index({ name: 'text', sku: 'text' });Samengestelde indexen ondersteunen queries die filteren of sorteren op meerdere velden. De index { category: 1, price: -1 } optimaliseert queries zoals find({ category }).sort({ price: -1 }).
Veelgestelde Sollicitatievragen over NestJS en MongoDB
Technische sollicitatiegesprekken testen zowel conceptueel begrip als praktische ervaring. Deze vragen verschijnen vaak bij senior backend posities.
V: Hoe behandelt Mongoose connection pooling?
Mongoose onderhoudt intern een connection pool. De maxPoolSize optie (standaard: 100) limiteert gelijktijdige verbindingen. Elke operatie haalt een verbinding uit de pool, voert deze uit en retourneert deze. Connection pooling vermijdt de overhead van het opzetten van nieuwe TCP-verbindingen per query.
V: Wanneer embedded documenten gebruiken in plaats van referenties?
Data embedden die bij elkaar hoort en beperkte groei heeft. Verzendadressen van een gebruiker (maximaal 5-10) embedden goed. Orderregels worden in het orderdocument geëmbed. Referenties passen bij onbegrensde relaties: bestellingen van een gebruiker over de jaren, of producten in een categorie. De regel: als de data 90% van de tijd samen wordt geladen en onder 16MB blijft, embedden.
V: Leg de $lookup stage van de aggregation pipeline uit.
De $lookup stage voert een left outer join uit tussen collections. Het matcht documenten uit een externe collection gebaseerd op veldgelijkheid of een aangepaste pipeline. Anders dan SQL joins wordt $lookup uitgevoerd tijdens de aggregatie en kan het extra filtering en projectie bevatten binnen de join.
// Example: Orders with customer details
const pipeline: PipelineStage[] = [
{
$lookup: {
from: 'customers',
localField: 'customerId',
foreignField: '_id',
as: 'customer',
},
},
{ $unwind: '$customer' },
];V: Hoe worden schema-migraties in MongoDB afgehandeld?
MongoDB schemas evolueren anders dan SQL. Gangbare strategieën:
- Nieuwe velden toevoegen met standaardwaarden (achterwaarts compatibel)
- Migratiescripts uitvoeren die bestaande documenten in batches bijwerken
- Schema-versioning gebruiken met een
schemaVersionveld - Mongoose middleware (
pre('save')) kan documenten transformeren bij schrijven
Senior kandidaten leggen trade-offs uit. Junioren sommen features op. Wanneer gevraagd wordt naar embedded vs gerefereerde documenten, bespreek querypatronen, documentgroottelimieten (16MB) en updatefrequentie, niet alleen "het hangt ervan af".
Foutafhandeling en Validatiepatronen
Mongoose validatie draait voor save-operaties. Aangepaste validators behandelen bedrijfslogica.
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
import { HydratedDocument } from 'mongoose';
export type OrderDocument = HydratedDocument<Order>;
@Schema({ timestamps: true })
export class Order {
@Prop({
required: true,
validate: {
validator: (v: number) => v > 0,
message: 'Amount must be positive',
},
})
amount: number;
@Prop({
type: String,
enum: ['pending', 'processing', 'completed', 'cancelled'],
default: 'pending',
})
status: string;
@Prop({ required: true })
items: OrderItem[];
}
export const OrderSchema = SchemaFactory.createForClass(Order);
// Pre-save hook for computed fields
OrderSchema.pre('save', function (next) {
// Recalculate total from items
if (this.isModified('items')) {
this.amount = this.items.reduce(
(sum, item) => sum + item.price * item.quantity,
0,
);
}
next();
});De pre('save') middleware draait voor elke save-operatie. Deze wordt gebruikt voor berekende velden, audit logging of cascaderende updates.
Performance Monitoring met explain()
De explain() methode onthult query-uitvoeringsplannen. Deze wordt gebruikt om ontbrekende indexen en trage queries te identificeren.
// Debug query performance
async analyzeQuery(category: string): Promise<void> {
const explanation = await this.productModel
.find({ category, inStock: true })
.sort({ price: -1 })
.explain('executionStats');
console.log('Documents examined:', explanation.executionStats.totalDocsExamined);
console.log('Documents returned:', explanation.executionStats.nReturned);
console.log('Execution time (ms):', explanation.executionStats.executionTimeMillis);
// If totalDocsExamined >> nReturned, add an index
}Een verhouding van totalDocsExamined tot nReturned dicht bij 1 duidt op efficiënt indexgebruik. Hoge verhoudingen signaleren ontbrekende indexen of niet-selectieve queries.
Kernpunten voor NestJS MongoDB Ontwikkeling
- Configureer
maxPoolSizeop basis van verwachte gelijktijdigheid, standaard 100 past bij de meeste workloads - Gebruik
HydratedDocument<T>voor correcte TypeScript typing van Mongoose documenten - Roep
.exec()aan op queries om native Promises te krijgen met accurate stack traces - Plaats
$matchstages vroeg in aggregation pipelines om verwerkte documenten te verminderen - Transacties vereisen replica sets, verifieer de deployment topologie voordat hierop wordt vertrouwd
- Maak samengestelde indexen die overeenkomen met querypatronen: filtervelden eerst, sorteervelden daarna
- Embed documenten met beperkte groei, refereer documenten die onbeperkt groeien
- Monitor query-prestaties met
explain('executionStats')tijdens ontwikkeling
Begin met oefenen!
Test je kennis met onze gespreksimulatoren en technische tests.
Zie jij de bug in Node.js / NestJS?
Een echt codefragment, een verborgen bug, één poging per dag. Zonder account uit te proberen.

Geschreven door
Anthony Fillion-MailletOprichter van SharpSkill
Al meer dan 10 jaar fullstack-ontwikkelaar. Hij leidt SharpSkill en staat in voor alles wat hier verschijnt.
Bijgewerkt op 28 augustus 2026
Delen
Gerelateerde artikelen

Snelle JavaScript ListView in 2026: Windowing, Virtualisatie en Performance-optimalisatie
Leer hoe je met windowing en virtualisatie performante JavaScript ListViews bouwt. TanStack Virtual, React Virtualized en geavanceerde technieken voor 2026.

NestJS en Redis in 2026: Caching, Sessies en Technische Sollicitatievragen
NestJS Redis-integratie met @nestjs/cache-manager, sessiebeheer met connect-redis en veelvoorkomende sollicitatievragen voor Node.js backend-ontwikkelaars.

High Performance JavaScript ListView in 2026: Virtualisatie en Optimalisatietechnieken
Ontdek hoe je met JavaScript list virtualization en React Virtualized hoogperformante ListViews ontwikkelt. Deep-dive in rendering optimalisatie voor grote datasets.