NestJS module that provides seamless integration between NestJS and Mongoose ODM for MongoDB database operations
80
Pending
Does it follow best practices?
Impact
80%
1.02xAverage score across 10 eval scenarios
Pending
The risk profile of this skill
Helper functions for generating dependency injection tokens, handling connection retry logic, and managing raw object definitions. These utilities support the core functionality of the NestJS Mongoose integration.
Utility functions for creating dependency injection tokens used by the NestJS container.
Generates the dependency injection token used for model injection.
/**
* Generates dependency injection token for a model
* @param model - Model name (typically the class name)
* @param connectionName - Optional connection name for multi-database setup
* @returns String token for dependency injection
*/
function getModelToken(model: string, connectionName?: string): string;Usage Examples:
import { getModelToken } from '@nestjs/mongoose';
// Basic model token
const userToken = getModelToken('User');
// Returns: 'UserModel'
// Model token with named connection
const userTokenWithConnection = getModelToken('User', 'users-db');
// Returns: 'users-db/UserModel'
// Use in custom providers
import { Module, Provider } from '@nestjs/common';
const customProviders: Provider[] = [
{
provide: 'USER_REPOSITORY',
useFactory: (userModel: Model<User>) => {
return new UserRepository(userModel);
},
inject: [getModelToken('User')],
},
{
provide: 'USER_CACHE_SERVICE',
useFactory: (userModel: Model<User>, cacheService: CacheService) => {
return new UserCacheService(userModel, cacheService);
},
inject: [getModelToken('User'), 'CACHE_SERVICE'],
},
];
@Module({
providers: [...customProviders],
exports: ['USER_REPOSITORY', 'USER_CACHE_SERVICE'],
})
export class UserRepositoryModule {}
// Testing with model tokens
import { Test, TestingModule } from '@nestjs/testing';
describe('UserService', () => {
let service: UserService;
let mockUserModel: Model<User>;
beforeEach(async () => {
const mockModel = {
find: jest.fn(),
findById: jest.fn(),
create: jest.fn(),
findByIdAndUpdate: jest.fn(),
findByIdAndDelete: jest.fn(),
};
const module: TestingModule = await Test.createTestingModule({
providers: [
UserService,
{
provide: getModelToken('User'),
useValue: mockModel,
},
],
}).compile();
service = module.get<UserService>(UserService);
mockUserModel = module.get<Model<User>>(getModelToken('User'));
});
it('should create a user', async () => {
const userData = { name: 'John Doe', email: 'john@example.com' };
mockUserModel.create = jest.fn().mockResolvedValue(userData);
const result = await service.create(userData);
expect(mockUserModel.create).toHaveBeenCalledWith(userData);
expect(result).toEqual(userData);
});
});Generates the dependency injection token used for connection injection.
/**
* Generates dependency injection token for a connection
* @param name - Optional connection name for multi-database setup
* @returns String token for dependency injection
*/
function getConnectionToken(name?: string): string;Usage Examples:
import { getConnectionToken } from '@nestjs/mongoose';
// Default connection token
const defaultToken = getConnectionToken();
// Returns: 'DatabaseConnection'
// Named connection token
const namedToken = getConnectionToken('analytics-db');
// Returns: 'analytics-db'
// Use in custom providers
const databaseProviders: Provider[] = [
{
provide: 'DATABASE_HEALTH_CHECK',
useFactory: (connection: Connection) => {
return new DatabaseHealthCheck(connection);
},
inject: [getConnectionToken()],
},
{
provide: 'ANALYTICS_DB_MANAGER',
useFactory: (connection: Connection) => {
return new DatabaseManager(connection);
},
inject: [getConnectionToken('analytics-db')],
},
];
// Multiple connections in service
@Injectable()
export class MultiDatabaseService {
constructor(
@Inject(getConnectionToken()) private defaultConnection: Connection,
@Inject(getConnectionToken('analytics-db')) private analyticsConnection: Connection,
) {}
async getConnectionInfo() {
return {
default: {
name: this.defaultConnection.name,
readyState: this.defaultConnection.readyState,
},
analytics: {
name: this.analyticsConnection.name,
readyState: this.analyticsConnection.readyState,
},
};
}
}RxJS operator for handling connection retry logic with configurable attempts and delays.
Creates an RxJS operator for connection retry logic.
/**
* RxJS operator for connection retry logic
* @param retryAttempts - Number of retry attempts (default: 9)
* @param retryDelay - Delay between retries in milliseconds (default: 3000)
* @param verboseRetryLog - Enable verbose retry logging (default: false)
* @returns RxJS operator function
*/
function handleRetry(
retryAttempts?: number,
retryDelay?: number,
verboseRetryLog?: boolean
): <T>(source: Observable<T>) => Observable<T>;Usage Examples:
import { handleRetry } from '@nestjs/mongoose';
import { Observable, throwError } from 'rxjs';
import { retryWhen } from 'rxjs/operators';
// Basic retry configuration
const connectWithRetry = (uri: string) => {
return mongoose.connect(uri).pipe(
retryWhen(handleRetry(5, 2000, true)) // 5 attempts, 2 second delay, verbose logging
);
};
// Custom connection service with retry logic
@Injectable()
export class ConnectionService {
private connect(uri: string): Observable<Connection> {
return new Observable(subscriber => {
mongoose.connect(uri)
.then(connection => {
subscriber.next(connection);
subscriber.complete();
})
.catch(error => {
subscriber.error(error);
});
}).pipe(
retryWhen(handleRetry(10, 5000, true)) // 10 attempts, 5 second delay
);
}
async establishConnection(uri: string): Promise<Connection> {
return this.connect(uri).toPromise();
}
}
// Integration with module configuration
export class DatabaseConnectionFactory {
static createConnectionProvider(
uri: string,
options: { retryAttempts?: number; retryDelay?: number; verboseRetryLog?: boolean } = {}
): Provider {
return {
provide: 'DATABASE_CONNECTION',
useFactory: async () => {
const { retryAttempts = 9, retryDelay = 3000, verboseRetryLog = false } = options;
return new Observable(subscriber => {
mongoose.connect(uri)
.then(connection => subscriber.next(connection))
.catch(error => subscriber.error(error));
}).pipe(
retryWhen(handleRetry(retryAttempts, retryDelay, verboseRetryLog))
).toPromise();
},
};
}
}Function for marking object definitions as raw to bypass type transformation.
Marks an object definition as raw, preventing Mongoose from applying type transformations.
/**
* Marks an object definition as raw (bypasses type transformation)
* @param definition - Object definition to mark as raw
* @returns Object definition with raw marker
*/
function raw(definition: Record<string, any>): Record<string, any>;Usage Examples:
import { raw } from '@nestjs/mongoose';
@Schema()
export class FlexibleDocument {
@Prop({ required: true })
name: string;
// Raw nested object - no type enforcement
@Prop(raw({
metadata: { type: mongoose.Schema.Types.Mixed },
settings: {
theme: { type: String, default: 'light' },
notifications: { type: Boolean, default: true },
preferences: mongoose.Schema.Types.Mixed
},
analytics: {
views: { type: Number, default: 0 },
clicks: { type: Number, default: 0 },
customEvents: [mongoose.Schema.Types.Mixed]
}
}))
dynamicData: {
metadata: any;
settings: {
theme: string;
notifications: boolean;
preferences: any;
};
analytics: {
views: number;
clicks: number;
customEvents: any[];
};
};
// Raw array of mixed objects
@Prop(raw([{
type: { type: String, required: true },
data: mongoose.Schema.Types.Mixed,
timestamp: { type: Date, default: Date.now }
}]))
events: Array<{
type: string;
data: any;
timestamp: Date;
}>;
}
// Complex raw schema for configuration
@Schema()
export class ApplicationConfig {
@Prop({ required: true })
appName: string;
@Prop(raw({
database: {
mongodb: {
uri: String,
options: mongoose.Schema.Types.Mixed
},
redis: {
host: String,
port: Number,
options: mongoose.Schema.Types.Mixed
}
},
services: [{
name: { type: String, required: true },
enabled: { type: Boolean, default: true },
config: mongoose.Schema.Types.Mixed
}],
features: mongoose.Schema.Types.Mixed,
customFields: mongoose.Schema.Types.Mixed
}))
configuration: {
database: {
mongodb: {
uri: string;
options: any;
};
redis: {
host: string;
port: number;
options: any;
};
};
services: Array<{
name: string;
enabled: boolean;
config: any;
}>;
features: any;
customFields: any;
};
}
// Usage in service
@Injectable()
export class ConfigService {
constructor(
@InjectModel(ApplicationConfig.name)
private configModel: Model<ApplicationConfig>
) {}
async updateConfig(appName: string, configUpdate: any): Promise<ApplicationConfig> {
// Raw objects allow any structure in configUpdate
return this.configModel.findOneAndUpdate(
{ appName },
{
$set: {
'configuration.features': configUpdate.features,
'configuration.customFields': configUpdate.customFields
}
},
{ new: true }
).exec();
}
async addService(appName: string, service: { name: string; config: any }): Promise<ApplicationConfig> {
return this.configModel.findOneAndUpdate(
{ appName },
{
$push: {
'configuration.services': {
name: service.name,
enabled: true,
config: service.config // Any structure allowed
}
}
},
{ new: true }
).exec();
}
}Pre-defined constants used throughout the NestJS Mongoose integration.
/** Default connection name when none is specified */
const DEFAULT_DB_CONNECTION = 'DatabaseConnection';
/** Injection token for Mongoose module options */
const MONGOOSE_MODULE_OPTIONS = 'MongooseModuleOptions';
/** Injection token for connection name */
const MONGOOSE_CONNECTION_NAME = 'MongooseConnectionName';
/** Symbol used to mark raw object definitions */
const RAW_OBJECT_DEFINITION = 'RAW_OBJECT_DEFINITION';Usage Examples:
import {
DEFAULT_DB_CONNECTION,
MONGOOSE_MODULE_OPTIONS,
MONGOOSE_CONNECTION_NAME,
RAW_OBJECT_DEFINITION
} from '@nestjs/mongoose';
// Check if using default connection
export function isDefaultConnection(connectionName?: string): boolean {
return !connectionName || connectionName === DEFAULT_DB_CONNECTION;
}
// Custom provider using constants
const configProvider: Provider = {
provide: 'CUSTOM_MONGOOSE_CONFIG',
useFactory: (options: MongooseModuleOptions, connectionName: string) => {
return {
options,
connectionName,
isDefault: connectionName === DEFAULT_DB_CONNECTION,
};
},
inject: [MONGOOSE_MODULE_OPTIONS, MONGOOSE_CONNECTION_NAME],
};
// Utility to check raw definitions
export function isRawDefinition(definition: any): boolean {
return definition && definition[RAW_OBJECT_DEFINITION] === true;
}export class DynamicTokenGenerator {
static generateModelTokens(models: string[], connectionName?: string): string[] {
return models.map(model => getModelToken(model, connectionName));
}
static generateConnectionTokens(connectionNames: string[]): string[] {
return connectionNames.map(name => getConnectionToken(name));
}
static createProviderMap(
models: string[],
connectionName?: string
): Record<string, string> {
return models.reduce((map, model) => {
map[model] = getModelToken(model, connectionName);
return map;
}, {} as Record<string, string>);
}
}
// Usage in dynamic modules
@Module({})
export class DynamicDatabaseModule {
static forFeature(models: string[], connectionName?: string): DynamicModule {
const tokens = DynamicTokenGenerator.generateModelTokens(models, connectionName);
const providerMap = DynamicTokenGenerator.createProviderMap(models, connectionName);
return {
module: DynamicDatabaseModule,
providers: [
{
provide: 'MODEL_TOKENS',
useValue: tokens,
},
{
provide: 'PROVIDER_MAP',
useValue: providerMap,
},
],
exports: ['MODEL_TOKENS', 'PROVIDER_MAP'],
};
}
}docs
evals
scenario-1
scenario-2
scenario-3
scenario-4
scenario-5
scenario-6
scenario-7
scenario-8
scenario-9
scenario-10