@wenu/nest-mongo
NestJS dynamic module for @wenu/mongo — typed MongoDB repository injection with graceful shutdown.
MongoDB 5/6/7 compatible. NestJS 10/11 compatible.
Table of Contents
- Features
- Installation
- Quick Start
- Module Setup
- Injecting Repositories and Connections
- Named Connections
- Index Synchronization
- Graceful Shutdown
- Health Checks
- Error Types
- API Reference
Features
- Zero-boilerplate DI —
@InjectRepository(UserCollection)wires a fully-typed repository into any NestJS service - forRoot / forRootAsync / forFeature — familiar NestJS dynamic module pattern
- Named connections — multiple MongoDB connections in the same app with full isolation
- Graceful shutdown —
OnApplicationShutdowncloses all connections with configurable timeout and retry - Index sync — optional
createIndexes()on module init, driven by theCollectionDefdeclaration - Health checks — opt-in
MongoHealthModulefor@nestjs/terminusintegration - Global module — providers registered once, available everywhere
Installation
# npm
npm install @wenu/nest-mongo @wenu/mongo
# pnpm
pnpm add @wenu/nest-mongo @wenu/mongoPeer dependencies
npm install @nestjs/common@^10 @nestjs/core@^10 mongodb@^6Requirements: Node >=18.0.0
Quick Start
// app.module.ts
import { Module } from '@nestjs/common';
import { MongoModule } from '@wenu/nest-mongo';
import { UserModule } from './user/user.module';
@Module({
imports: [
MongoModule.forRoot({
uri: 'mongodb://localhost:27017',
databaseName: 'myapp',
}),
UserModule,
],
})
export class AppModule {}// user/user.module.ts
import { Module } from '@nestjs/common';
import { MongoModule } from '@wenu/nest-mongo';
import { UserCollection } from './user.collection';
import { UserService } from './user.service';
@Module({
imports: [MongoModule.forFeature([UserCollection])],
providers: [UserService],
})
export class UserModule {}// user/user.service.ts
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@wenu/nest-mongo';
import type { Repository } from '@wenu/nest-mongo';
import { UserCollection } from './user.collection';
type UserRepo = Repository<typeof UserCollection.schema, 'objectid'>;
@Injectable()
export class UserService {
constructor(@InjectRepository(UserCollection) private readonly users: UserRepo) {}
async create(name: string, email: string) {
return this.users.insert({ name, email, createdAt: new Date() });
}
async exists(email: string) {
const result = await this.users.exists({ email });
return result.ok ? result.value : false;
}
async count() {
const result = await this.users.count();
return result.ok ? result.value : 0;
}
async upsert(id: string, name: string, email: string) {
// Insert if not found, replace if found — _id always preserved
return this.users.upsertById(id, { name, email, createdAt: new Date() });
}
}Module Setup
forRoot — Static Configuration
Use when connection options are available at module load time.
MongoModule.forRoot({
uri: 'mongodb://localhost:27017',
databaseName: 'myapp',
});Options
| Option | Type | Default | Description |
|---|---|---|---|
uri |
string |
— | MongoDB connection URI (mutually exclusive with mongoClient) |
mongoClient |
MongoClient |
— | Pre-built client (mutually exclusive with uri) |
databaseName |
string |
— | Database name |
connectionName |
string | symbol |
'default' |
Token namespace for named connections |
syncIndexes |
boolean |
true |
Call createIndexes() on module init |
clientOptions |
MongoClientOptions |
— | Passed to new MongoClient() (only with uri) |
shutdownTimeoutMs |
number |
10_000 |
Max ms to wait for MongoClient.close() |
shutdownRetryAttempts |
number |
2 |
Retry attempts on close failure |
forceShutdown |
boolean |
false |
Pass force: true to MongoClient.close() |
forRootAsync — Factory Configuration
Use when options depend on a config service, environment variables, or other async sources.
import { ConfigService } from '@nestjs/config';
MongoModule.forRootAsync({
imports: [ConfigModule],
useFactory: (config: ConfigService) => ({
uri: config.get<string>('MONGO_URI'),
databaseName: config.get<string>('MONGO_DB'),
}),
inject: [ConfigService],
});forFeature — Repository Registration
Register one or more collections in a feature module. Optionally scope to a named connection.
// Default connection
MongoModule.forFeature([UserCollection, PostCollection]);
// Named connection
MongoModule.forFeature([OrderCollection], 'analytics');Each collection gets a repository provider keyed by
getRepositoryToken(collection.name, connectionName), injectable via @InjectRepository.
Injecting Repositories and Connections
@InjectRepository
Inject a typed repository for a collection. Accepts a CollectionDef or a plain string name.
// With CollectionDef (recommended — type-safe)
@InjectRepository(UserCollection) private readonly users: UserRepo
// With plain string
@InjectRepository('users') private readonly users: UserRepo
// Named connection
@InjectRepository(OrderCollection, 'analytics') private readonly orders: OrderRepo@InjectConnection
Inject the raw Db handle for direct driver access.
import { InjectConnection } from '@wenu/nest-mongo'
import type { Db } from 'mongodb'
@Injectable()
export class AdminService {
constructor(@InjectConnection() private readonly db: Db) {}
async stats() {
return this.db.stats()
}
}
// Named connection
@InjectConnection('analytics') private readonly analyticsDb: DbNamed Connections
Register multiple forRoot calls with distinct connectionName values. Each connection manages its
own MongoClient, Db, and shutdown lifecycle independently.
@Module({
imports: [
MongoModule.forRoot({
uri: process.env.PRIMARY_MONGO_URI,
databaseName: 'app',
connectionName: 'primary',
}),
MongoModule.forRoot({
uri: process.env.ANALYTICS_MONGO_URI,
databaseName: 'analytics',
connectionName: 'analytics',
}),
UserModule,
ReportingModule,
],
})
export class AppModule {}// user.module.ts
MongoModule.forFeature([UserCollection], 'primary');
// reporting.module.ts
MongoModule.forFeature([EventCollection], 'analytics');Index Synchronization
When syncIndexes: true (default), the module calls createIndexes() for every collection
registered via forFeature. Indexes are declared in defineCollection() using the index() helper
from @wenu/mongo.
import { defineCollection, index } from '@wenu/nest-mongo';
import * as z from 'zod';
const UserCollection = defineCollection({
name: 'users',
schema: z.object({ email: z.string(), name: z.string() }),
indexes: [index({ email: 1 }, { unique: true })],
});MongoDB skips indexes that already exist — safe to run on every startup.
To disable: MongoModule.forRoot({ ..., syncIndexes: false }).
Graceful Shutdown
MongoModule implements OnApplicationShutdown. When app.close() is called, all registered
MongoClient instances are closed in parallel with timeout and retry protection.
Enable shutdown hooks in your bootstrap:
const app = await NestFactory.create(AppModule);
app.enableShutdownHooks();
await app.listen(3000);Configure shutdown behavior via forRoot options:
MongoModule.forRoot({
uri: '...',
databaseName: 'myapp',
shutdownTimeoutMs: 5_000, // give each close() 5 seconds
shutdownRetryAttempts: 3, // retry up to 3 times on failure
forceShutdown: false, // set true to force-close (drops in-flight ops)
});Health Checks
MongoHealthModule provides an opt-in MongoHealthIndicator that integrates with
@nestjs/terminus. It requires @nestjs/terminus >=10.0.0 as a peer dependency — only install it
if you use health checks.
npm install @nestjs/terminus// health/health.module.ts
import { Module } from '@nestjs/common';
import { TerminusModule } from '@nestjs/terminus';
import { MongoHealthModule } from '@wenu/nest-mongo';
@Module({
imports: [TerminusModule, MongoHealthModule],
})
export class HealthModule {}// health/health.controller.ts
import { Controller, Get } from '@nestjs/common';
import { HealthCheck, HealthCheckService } from '@nestjs/terminus';
import { InjectConnection } from '@wenu/nest-mongo';
import { MongoHealthIndicator } from '@wenu/nest-mongo';
import type { Db } from 'mongodb';
@Controller('health')
export class HealthController {
constructor(
private readonly health: HealthCheckService,
private readonly mongoHealth: MongoHealthIndicator,
@InjectConnection() private readonly db: Db,
) {}
@Get()
@HealthCheck()
check() {
return this.health.check([() => this.mongoHealth.isHealthy('mongodb', this.db)]);
}
}The indicator calls db.command({ ping: 1 }) and returns { status: 'up' } on success or
{ status: 'down', error } on failure.
Error Types
| Error | When thrown |
|---|---|
MongoConnectionError |
MongoClient.connect() fails during module init |
MongoConfigurationError |
Neither uri nor mongoClient provided in options |
Both extend Error and are exported for use in catch blocks or NestJS exception filters.
import { MongoConnectionError } from '@wenu/nest-mongo';
try {
await app.init();
} catch (error) {
if (error instanceof MongoConnectionError) {
console.error('Could not connect to MongoDB:', error.message);
}
}API Reference
MongoModule
| Method | Returns | Description |
|---|---|---|
forRoot(options) |
DynamicModule |
Static connection setup |
forRootAsync(asyncOptions) |
DynamicModule |
Factory-based connection setup |
forFeature(collections, name?) |
DynamicModule |
Register repository providers in a feature module |
Token helpers
import { getRepositoryToken, getConnectionToken, DEFAULT_CONNECTION } from '@wenu/nest-mongo';
getRepositoryToken('users'); // 'usersRepository'
getRepositoryToken('users', 'analytics'); // 'analytics_usersRepository'
getConnectionToken(); // DEFAULT_CONNECTION symbol
getConnectionToken('analytics'); // 'analytics'MongoHealthModule
| Export | Description |
|---|---|
MongoHealthModule |
Opt-in NestJS module — import alongside TerminusModule |
MongoHealthIndicator |
Injectable indicator — call isHealthy(key, db) in a health check |
Requires @nestjs/terminus >=10.0.0 as an optional peer dependency.
Re-exports from @wenu/mongo
For convenience, the following are re-exported so you don't need to install @wenu/mongo separately
for common types:
import { defineCollection, ok, err, isOk, isErr, NotFoundError } from '@wenu/nest-mongo';
import { MongoHealthIndicator, MongoHealthModule } from '@wenu/nest-mongo';
import type { Repository, CollectionDef, Doc, Result, DbError } from '@wenu/nest-mongo';License
MIT