0
# Utility Functions
1
2
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.
3
4
## Capabilities
5
6
### Token Generation Functions
7
8
Utility functions for creating dependency injection tokens used by the NestJS container.
9
10
#### getModelToken
11
12
Generates the dependency injection token used for model injection.
13
14
```typescript { .api }
15
/**
16
* Generates dependency injection token for a model
17
* @param model - Model name (typically the class name)
18
* @param connectionName - Optional connection name for multi-database setup
19
* @returns String token for dependency injection
20
*/
21
function getModelToken(model: string, connectionName?: string): string;
22
```
23
24
**Usage Examples:**
25
26
```typescript
27
import { getModelToken } from '@nestjs/mongoose';
28
29
// Basic model token
30
const userToken = getModelToken('User');
31
// Returns: 'UserModel'
32
33
// Model token with named connection
34
const userTokenWithConnection = getModelToken('User', 'users-db');
35
// Returns: 'users-db/UserModel'
36
37
// Use in custom providers
38
import { Module, Provider } from '@nestjs/common';
39
40
const customProviders: Provider[] = [
41
{
42
provide: 'USER_REPOSITORY',
43
useFactory: (userModel: Model<User>) => {
44
return new UserRepository(userModel);
45
},
46
inject: [getModelToken('User')],
47
},
48
{
49
provide: 'USER_CACHE_SERVICE',
50
useFactory: (userModel: Model<User>, cacheService: CacheService) => {
51
return new UserCacheService(userModel, cacheService);
52
},
53
inject: [getModelToken('User'), 'CACHE_SERVICE'],
54
},
55
];
56
57
@Module({
58
providers: [...customProviders],
59
exports: ['USER_REPOSITORY', 'USER_CACHE_SERVICE'],
60
})
61
export class UserRepositoryModule {}
62
63
// Testing with model tokens
64
import { Test, TestingModule } from '@nestjs/testing';
65
66
describe('UserService', () => {
67
let service: UserService;
68
let mockUserModel: Model<User>;
69
70
beforeEach(async () => {
71
const mockModel = {
72
find: jest.fn(),
73
findById: jest.fn(),
74
create: jest.fn(),
75
findByIdAndUpdate: jest.fn(),
76
findByIdAndDelete: jest.fn(),
77
};
78
79
const module: TestingModule = await Test.createTestingModule({
80
providers: [
81
UserService,
82
{
83
provide: getModelToken('User'),
84
useValue: mockModel,
85
},
86
],
87
}).compile();
88
89
service = module.get<UserService>(UserService);
90
mockUserModel = module.get<Model<User>>(getModelToken('User'));
91
});
92
93
it('should create a user', async () => {
94
const userData = { name: 'John Doe', email: 'john@example.com' };
95
mockUserModel.create = jest.fn().mockResolvedValue(userData);
96
97
const result = await service.create(userData);
98
expect(mockUserModel.create).toHaveBeenCalledWith(userData);
99
expect(result).toEqual(userData);
100
});
101
});
102
```
103
104
#### getConnectionToken
105
106
Generates the dependency injection token used for connection injection.
107
108
```typescript { .api }
109
/**
110
* Generates dependency injection token for a connection
111
* @param name - Optional connection name for multi-database setup
112
* @returns String token for dependency injection
113
*/
114
function getConnectionToken(name?: string): string;
115
```
116
117
**Usage Examples:**
118
119
```typescript
120
import { getConnectionToken } from '@nestjs/mongoose';
121
122
// Default connection token
123
const defaultToken = getConnectionToken();
124
// Returns: 'DatabaseConnection'
125
126
// Named connection token
127
const namedToken = getConnectionToken('analytics-db');
128
// Returns: 'analytics-db'
129
130
// Use in custom providers
131
const databaseProviders: Provider[] = [
132
{
133
provide: 'DATABASE_HEALTH_CHECK',
134
useFactory: (connection: Connection) => {
135
return new DatabaseHealthCheck(connection);
136
},
137
inject: [getConnectionToken()],
138
},
139
{
140
provide: 'ANALYTICS_DB_MANAGER',
141
useFactory: (connection: Connection) => {
142
return new DatabaseManager(connection);
143
},
144
inject: [getConnectionToken('analytics-db')],
145
},
146
];
147
148
// Multiple connections in service
149
@Injectable()
150
export class MultiDatabaseService {
151
constructor(
152
@Inject(getConnectionToken()) private defaultConnection: Connection,
153
@Inject(getConnectionToken('analytics-db')) private analyticsConnection: Connection,
154
) {}
155
156
async getConnectionInfo() {
157
return {
158
default: {
159
name: this.defaultConnection.name,
160
readyState: this.defaultConnection.readyState,
161
},
162
analytics: {
163
name: this.analyticsConnection.name,
164
readyState: this.analyticsConnection.readyState,
165
},
166
};
167
}
168
}
169
```
170
171
### Connection Retry Utility
172
173
RxJS operator for handling connection retry logic with configurable attempts and delays.
174
175
#### handleRetry
176
177
Creates an RxJS operator for connection retry logic.
178
179
```typescript { .api }
180
/**
181
* RxJS operator for connection retry logic
182
* @param retryAttempts - Number of retry attempts (default: 9)
183
* @param retryDelay - Delay between retries in milliseconds (default: 3000)
184
* @param verboseRetryLog - Enable verbose retry logging (default: false)
185
* @returns RxJS operator function
186
*/
187
function handleRetry(
188
retryAttempts?: number,
189
retryDelay?: number,
190
verboseRetryLog?: boolean
191
): <T>(source: Observable<T>) => Observable<T>;
192
```
193
194
**Usage Examples:**
195
196
```typescript
197
import { handleRetry } from '@nestjs/mongoose';
198
import { Observable, throwError } from 'rxjs';
199
import { retryWhen } from 'rxjs/operators';
200
201
// Basic retry configuration
202
const connectWithRetry = (uri: string) => {
203
return mongoose.connect(uri).pipe(
204
retryWhen(handleRetry(5, 2000, true)) // 5 attempts, 2 second delay, verbose logging
205
);
206
};
207
208
// Custom connection service with retry logic
209
@Injectable()
210
export class ConnectionService {
211
private connect(uri: string): Observable<Connection> {
212
return new Observable(subscriber => {
213
mongoose.connect(uri)
214
.then(connection => {
215
subscriber.next(connection);
216
subscriber.complete();
217
})
218
.catch(error => {
219
subscriber.error(error);
220
});
221
}).pipe(
222
retryWhen(handleRetry(10, 5000, true)) // 10 attempts, 5 second delay
223
);
224
}
225
226
async establishConnection(uri: string): Promise<Connection> {
227
return this.connect(uri).toPromise();
228
}
229
}
230
231
// Integration with module configuration
232
export class DatabaseConnectionFactory {
233
static createConnectionProvider(
234
uri: string,
235
options: { retryAttempts?: number; retryDelay?: number; verboseRetryLog?: boolean } = {}
236
): Provider {
237
return {
238
provide: 'DATABASE_CONNECTION',
239
useFactory: async () => {
240
const { retryAttempts = 9, retryDelay = 3000, verboseRetryLog = false } = options;
241
242
return new Observable(subscriber => {
243
mongoose.connect(uri)
244
.then(connection => subscriber.next(connection))
245
.catch(error => subscriber.error(error));
246
}).pipe(
247
retryWhen(handleRetry(retryAttempts, retryDelay, verboseRetryLog))
248
).toPromise();
249
},
250
};
251
}
252
}
253
```
254
255
### Raw Object Definition Utility
256
257
Function for marking object definitions as raw to bypass type transformation.
258
259
#### raw
260
261
Marks an object definition as raw, preventing Mongoose from applying type transformations.
262
263
```typescript { .api }
264
/**
265
* Marks an object definition as raw (bypasses type transformation)
266
* @param definition - Object definition to mark as raw
267
* @returns Object definition with raw marker
268
*/
269
function raw(definition: Record<string, any>): Record<string, any>;
270
```
271
272
**Usage Examples:**
273
274
```typescript
275
import { raw } from '@nestjs/mongoose';
276
277
@Schema()
278
export class FlexibleDocument {
279
@Prop({ required: true })
280
name: string;
281
282
// Raw nested object - no type enforcement
283
@Prop(raw({
284
metadata: { type: mongoose.Schema.Types.Mixed },
285
settings: {
286
theme: { type: String, default: 'light' },
287
notifications: { type: Boolean, default: true },
288
preferences: mongoose.Schema.Types.Mixed
289
},
290
analytics: {
291
views: { type: Number, default: 0 },
292
clicks: { type: Number, default: 0 },
293
customEvents: [mongoose.Schema.Types.Mixed]
294
}
295
}))
296
dynamicData: {
297
metadata: any;
298
settings: {
299
theme: string;
300
notifications: boolean;
301
preferences: any;
302
};
303
analytics: {
304
views: number;
305
clicks: number;
306
customEvents: any[];
307
};
308
};
309
310
// Raw array of mixed objects
311
@Prop(raw([{
312
type: { type: String, required: true },
313
data: mongoose.Schema.Types.Mixed,
314
timestamp: { type: Date, default: Date.now }
315
}]))
316
events: Array<{
317
type: string;
318
data: any;
319
timestamp: Date;
320
}>;
321
}
322
323
// Complex raw schema for configuration
324
@Schema()
325
export class ApplicationConfig {
326
@Prop({ required: true })
327
appName: string;
328
329
@Prop(raw({
330
database: {
331
mongodb: {
332
uri: String,
333
options: mongoose.Schema.Types.Mixed
334
},
335
redis: {
336
host: String,
337
port: Number,
338
options: mongoose.Schema.Types.Mixed
339
}
340
},
341
services: [{
342
name: { type: String, required: true },
343
enabled: { type: Boolean, default: true },
344
config: mongoose.Schema.Types.Mixed
345
}],
346
features: mongoose.Schema.Types.Mixed,
347
customFields: mongoose.Schema.Types.Mixed
348
}))
349
configuration: {
350
database: {
351
mongodb: {
352
uri: string;
353
options: any;
354
};
355
redis: {
356
host: string;
357
port: number;
358
options: any;
359
};
360
};
361
services: Array<{
362
name: string;
363
enabled: boolean;
364
config: any;
365
}>;
366
features: any;
367
customFields: any;
368
};
369
}
370
371
// Usage in service
372
@Injectable()
373
export class ConfigService {
374
constructor(
375
@InjectModel(ApplicationConfig.name)
376
private configModel: Model<ApplicationConfig>
377
) {}
378
379
async updateConfig(appName: string, configUpdate: any): Promise<ApplicationConfig> {
380
// Raw objects allow any structure in configUpdate
381
return this.configModel.findOneAndUpdate(
382
{ appName },
383
{
384
$set: {
385
'configuration.features': configUpdate.features,
386
'configuration.customFields': configUpdate.customFields
387
}
388
},
389
{ new: true }
390
).exec();
391
}
392
393
async addService(appName: string, service: { name: string; config: any }): Promise<ApplicationConfig> {
394
return this.configModel.findOneAndUpdate(
395
{ appName },
396
{
397
$push: {
398
'configuration.services': {
399
name: service.name,
400
enabled: true,
401
config: service.config // Any structure allowed
402
}
403
}
404
},
405
{ new: true }
406
).exec();
407
}
408
}
409
```
410
411
## Constants
412
413
Pre-defined constants used throughout the NestJS Mongoose integration.
414
415
```typescript { .api }
416
/** Default connection name when none is specified */
417
const DEFAULT_DB_CONNECTION = 'DatabaseConnection';
418
419
/** Injection token for Mongoose module options */
420
const MONGOOSE_MODULE_OPTIONS = 'MongooseModuleOptions';
421
422
/** Injection token for connection name */
423
const MONGOOSE_CONNECTION_NAME = 'MongooseConnectionName';
424
425
/** Symbol used to mark raw object definitions */
426
const RAW_OBJECT_DEFINITION = 'RAW_OBJECT_DEFINITION';
427
```
428
429
**Usage Examples:**
430
431
```typescript
432
import {
433
DEFAULT_DB_CONNECTION,
434
MONGOOSE_MODULE_OPTIONS,
435
MONGOOSE_CONNECTION_NAME,
436
RAW_OBJECT_DEFINITION
437
} from '@nestjs/mongoose';
438
439
// Check if using default connection
440
export function isDefaultConnection(connectionName?: string): boolean {
441
return !connectionName || connectionName === DEFAULT_DB_CONNECTION;
442
}
443
444
// Custom provider using constants
445
const configProvider: Provider = {
446
provide: 'CUSTOM_MONGOOSE_CONFIG',
447
useFactory: (options: MongooseModuleOptions, connectionName: string) => {
448
return {
449
options,
450
connectionName,
451
isDefault: connectionName === DEFAULT_DB_CONNECTION,
452
};
453
},
454
inject: [MONGOOSE_MODULE_OPTIONS, MONGOOSE_CONNECTION_NAME],
455
};
456
457
// Utility to check raw definitions
458
export function isRawDefinition(definition: any): boolean {
459
return definition && definition[RAW_OBJECT_DEFINITION] === true;
460
}
461
```
462
463
## Advanced Utility Patterns
464
465
### Token Generation for Dynamic Modules
466
467
```typescript { .api }
468
export class DynamicTokenGenerator {
469
static generateModelTokens(models: string[], connectionName?: string): string[] {
470
return models.map(model => getModelToken(model, connectionName));
471
}
472
473
static generateConnectionTokens(connectionNames: string[]): string[] {
474
return connectionNames.map(name => getConnectionToken(name));
475
}
476
477
static createProviderMap(
478
models: string[],
479
connectionName?: string
480
): Record<string, string> {
481
return models.reduce((map, model) => {
482
map[model] = getModelToken(model, connectionName);
483
return map;
484
}, {} as Record<string, string>);
485
}
486
}
487
488
// Usage in dynamic modules
489
@Module({})
490
export class DynamicDatabaseModule {
491
static forFeature(models: string[], connectionName?: string): DynamicModule {
492
const tokens = DynamicTokenGenerator.generateModelTokens(models, connectionName);
493
const providerMap = DynamicTokenGenerator.createProviderMap(models, connectionName);
494
495
return {
496
module: DynamicDatabaseModule,
497
providers: [
498
{
499
provide: 'MODEL_TOKENS',
500
useValue: tokens,
501
},
502
{
503
provide: 'PROVIDER_MAP',
504
useValue: providerMap,
505
},
506
],
507
exports: ['MODEL_TOKENS', 'PROVIDER_MAP'],
508
};
509
}
510
}
511
```