@common-stack/store-mongo 7.0.4-alpha.6 → 7.1.1-alpha.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/lib/containers/container.d.ts +14 -0
  2. package/lib/containers/container.js +17 -0
  3. package/lib/containers/container.js.map +1 -0
  4. package/lib/containers/index.d.ts +1 -0
  5. package/lib/dataloaders/bulk-dataloader-v2.d.ts +7 -0
  6. package/lib/dataloaders/bulk-dataloader-v2.js +34 -0
  7. package/lib/dataloaders/bulk-dataloader-v2.js.map +1 -0
  8. package/lib/dataloaders/bulk-dataloader.js.map +1 -1
  9. package/lib/dataloaders/bulk-dataloader.test.d.ts +1 -0
  10. package/lib/dataloaders/index.d.ts +1 -0
  11. package/lib/graphql/schema/base-services.graphql +238 -0
  12. package/lib/helpers/mongoose-connection.js.map +1 -1
  13. package/lib/index.d.ts +1 -1
  14. package/lib/index.js +1 -1
  15. package/lib/interfaces/base-repository.d.ts +1 -5
  16. package/lib/interfaces/base-service.d.ts +1 -1
  17. package/lib/interfaces/getAllArgs.d.ts +133 -0
  18. package/lib/interfaces/getAllArgs.js +39 -0
  19. package/lib/interfaces/getAllArgs.js.map +1 -0
  20. package/lib/interfaces/index.d.ts +1 -1
  21. package/lib/mixins/BaseServiceMixin.d.ts +45 -0
  22. package/lib/mixins/BaseServiceMixin.js +199 -0
  23. package/lib/mixins/BaseServiceMixin.js.map +1 -0
  24. package/lib/mixins/base-service-mixin.js.map +1 -1
  25. package/lib/mixins/index.d.ts +1 -0
  26. package/lib/module.d.ts +3 -0
  27. package/lib/module.js +4 -0
  28. package/lib/module.js.map +1 -0
  29. package/lib/services/BaseProxyService.d.ts +28 -0
  30. package/lib/services/BaseProxyService.js +52 -0
  31. package/lib/services/BaseProxyService.js.map +1 -0
  32. package/lib/services/BaseService.d.ts +23 -0
  33. package/lib/services/BaseService.js +65 -0
  34. package/lib/services/BaseService.js.map +1 -0
  35. package/lib/services/BaseService.test.d.ts +1 -0
  36. package/lib/services/base-proxy-service.js.map +1 -1
  37. package/lib/services/base-service.js.map +1 -1
  38. package/lib/services/index.d.ts +2 -0
  39. package/lib/store/models/common-options-v2.d.ts +16 -0
  40. package/lib/store/models/common-options-v2.js +40 -0
  41. package/lib/store/models/common-options-v2.js.map +1 -0
  42. package/lib/store/models/common-options.js.map +1 -1
  43. package/lib/store/models/index.d.ts +1 -0
  44. package/lib/store/repositories/BaseMongoRepository.d.ts +65 -0
  45. package/lib/store/repositories/BaseMongoRepository.js +389 -0
  46. package/lib/store/repositories/BaseMongoRepository.js.map +1 -0
  47. package/lib/store/repositories/BaseMongoRepository.test.d.ts +1 -0
  48. package/lib/store/repositories/CustomIdRepository.test.d.ts +1 -0
  49. package/lib/store/repositories/base-repository.js +1 -1
  50. package/lib/store/repositories/base-repository.js.map +1 -1
  51. package/lib/store/repositories/index.d.ts +1 -0
  52. package/lib/templates/constants/SERVER_TYPES.ts.template +5 -0
  53. package/lib/templates/repositories/DatabaseMigration.ts.template +13 -0
  54. package/lib/templates/repositories/IBaseMongoRepository.ts.template +284 -0
  55. package/lib/templates/repositories/IBaseService.ts.template +231 -0
  56. package/lib/templates/repositories/IBaseServiceMixin.ts.template +461 -0
  57. package/lib/templates/repositories/IDataloader.ts.template +211 -0
  58. package/lib/templates/repositories/dbCommonTypes.ts.template +139 -0
  59. package/lib/templates/repositories/mongoCommonTypes.ts.template +21 -0
  60. package/package.json +21 -4
  61. package/lib/interfaces/base-repository.js +0 -5
  62. package/lib/interfaces/base-repository.js.map +0 -1
  63. package/lib/interfaces/get-all-args.d.ts +0 -9
@@ -0,0 +1,461 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+ /**
3
+ * @file IBaseServiceMixin.ts
4
+ * @description Defines the interface and types for the BaseServiceMixin functionality.
5
+ * This file documents the contract and behavior of the BaseServiceMixin without implementation details.
6
+ *
7
+ * The BaseServiceMixin provides a way to add standardized service methods to Moleculer service schemas.
8
+ * It implements the IBaseService interface and connects it to Moleculer's service framework,
9
+ * creating action handlers for all standard service operations.
10
+ *
11
+ * Architecture Context:
12
+ * - Part of the service layer in a microservices architecture
13
+ * - Bridges domain services with the Moleculer microservice framework
14
+ * - Follows a consistent pattern for exposing domain operations as microservice actions
15
+ * - Implements an event-driven architecture by emitting events on data changes
16
+ * - Provides automatic parameter validation through Moleculer's built-in validator
17
+ *
18
+ * Key features:
19
+ * - Automatic action creation for all IBaseService methods
20
+ * - Event emission for data changes (create, update, delete)
21
+ * - Parameter validation for all actions
22
+ * - Consistent error handling
23
+ * - Type safety through generics
24
+ *
25
+ * Usage Patterns:
26
+ * - Include this mixin in any Moleculer service that needs standard CRUD operations
27
+ * - Customize event emission by providing a custom events array
28
+ * - Extend with additional service-specific actions
29
+ * - Use with any implementation of IBaseService (typically a concrete service class)
30
+ *
31
+ * @see IBaseService - The interface this mixin implements
32
+ * @see IBaseMongoRepository - The repository interface used for data access
33
+ * @see BaseServiceCommands - Enum of standard service commands used as action names
34
+ */
35
+
36
+ import type { ServiceBroker, ServiceSchema } from 'moleculer';
37
+ import { BaseServiceCommands } from 'common';
38
+ import { IBaseService } from './IBaseService';
39
+ import { CreateType, UpdateType } from './dbCommonTypes';
40
+
41
+
42
+ /**
43
+ * Interface for the BaseServiceMixin function
44
+ * This function creates a Moleculer service mixin that implements the IBaseService interface
45
+ *
46
+ * @param service - Implementation of IBaseService that will handle the actual business logic
47
+ * @param broker - Moleculer service broker for event emission
48
+ * @param name - Service name used for event naming
49
+ * @param events - List of operations that should emit events
50
+ * @returns A partial ServiceSchema that can be merged with other service definitions
51
+ *
52
+ * @example
53
+ * // Create a user service with the base service mixin
54
+ * const UserService: ServiceSchema = {
55
+ * name: "users",
56
+ * mixins: [
57
+ * BaseServiceMixin(userService, broker, "users")
58
+ * ],
59
+ * // Additional service-specific actions and methods
60
+ * actions: {
61
+ * changePassword: { ... }
62
+ * }
63
+ * };
64
+ *
65
+ * // Example of customizing event emission
66
+ * const ProductService: ServiceSchema = {
67
+ * name: "products",
68
+ * mixins: [
69
+ * // Only emit events for create and update operations
70
+ * BaseServiceMixin(
71
+ * productService,
72
+ * broker,
73
+ * "products",
74
+ * [BaseServiceCommands.Create, BaseServiceCommands.Update]
75
+ * )
76
+ * ]
77
+ * };
78
+ */
79
+ export interface IBaseServiceMixinFunction {
80
+ <T, C = CreateType<T>, U = UpdateType<T>>(
81
+ service: IBaseService<T, C, U>,
82
+ broker?: ServiceBroker,
83
+ name?: string,
84
+ events?: BaseServiceCommands[],
85
+ ): Partial<ServiceSchema>;
86
+ }
87
+
88
+ /**
89
+ * Interface describing the actions created by the BaseServiceMixin
90
+ * Each action corresponds to a method in the IBaseService interface
91
+ */
92
+ export interface IBaseServiceMixinActions {
93
+ /**
94
+ * Get a single document by ID
95
+ *
96
+ * This action retrieves a single entity by its unique identifier.
97
+ * It maps to the IBaseService.get method and handles parameter validation.
98
+ *
99
+ * @param id - The unique identifier of the document to retrieve
100
+ * @returns The domain entity or null if not found
101
+ *
102
+ * @example
103
+ * // Call from another service
104
+ * const user = await broker.call('users.get', { id: '123' });
105
+ *
106
+ * // Call from API
107
+ * app.get('/api/users/:id', async (req, res) => {
108
+ * const user = await broker.call('users.get', { id: req.params.id });
109
+ * res.json(user);
110
+ * });
111
+ */
112
+ [BaseServiceCommands.Get]: {
113
+ params: {
114
+ id: string;
115
+ };
116
+ };
117
+
118
+ /**
119
+ * Count documents matching criteria
120
+ *
121
+ * This action counts entities matching the provided filter criteria.
122
+ * It maps to the IBaseService.count method and handles parameter validation.
123
+ *
124
+ * @param criteria - Optional filter criteria to count specific documents
125
+ * @returns The count of matching documents
126
+ *
127
+ * @example
128
+ * // Count active users
129
+ * const count = await broker.call('users.count', {
130
+ * criteria: { active: true }
131
+ * });
132
+ */
133
+ [BaseServiceCommands.Count]: {
134
+ params: {
135
+ criteria?: Record<string, any>;
136
+ };
137
+ };
138
+
139
+ /**
140
+ * Create multiple documents
141
+ *
142
+ * This action creates multiple entities in a single operation.
143
+ * It maps to the IBaseService.bulkCreate method and handles parameter validation.
144
+ * It also emits events for the created entities if configured.
145
+ *
146
+ * @param data - Array of data objects for the new documents
147
+ * @returns Array of created domain entities
148
+ *
149
+ * @example
150
+ * // Create multiple users
151
+ * const users = await broker.call('users.bulkCreate', {
152
+ * data: [
153
+ * { name: 'John', email: 'john@example.com' },
154
+ * { name: 'Jane', email: 'jane@example.com' }
155
+ * ]
156
+ * });
157
+ */
158
+ [BaseServiceCommands.BulkCreate]: {
159
+ params: {
160
+ data: any[];
161
+ };
162
+ };
163
+
164
+ /**
165
+ * Create a single document
166
+ *
167
+ * This action creates a single entity.
168
+ * It maps to the IBaseService.create method and handles parameter validation.
169
+ * It also emits an event for the created entity if configured.
170
+ *
171
+ * @param data - Data object for the new document
172
+ * @returns The created domain entity
173
+ *
174
+ * @example
175
+ * // Create a new user
176
+ * const user = await broker.call('users.create', {
177
+ * data: { name: 'John', email: 'john@example.com' }
178
+ * });
179
+ */
180
+ [BaseServiceCommands.Create]: {
181
+ params: {
182
+ data: any;
183
+ };
184
+ };
185
+
186
+ /**
187
+ * Delete a document by ID
188
+ *
189
+ * This action deletes a single entity by its unique identifier.
190
+ * It maps to the IBaseService.delete method and handles parameter validation.
191
+ * It also emits an event for the deleted entity if configured.
192
+ *
193
+ * @param id - The unique identifier of the document to delete
194
+ * @returns Boolean indicating success
195
+ *
196
+ * @example
197
+ * // Delete a user
198
+ * const success = await broker.call('users.delete', { id: '123' });
199
+ */
200
+ [BaseServiceCommands.Delete]: {
201
+ params: {
202
+ id: string;
203
+ };
204
+ };
205
+
206
+ /**
207
+ * Get documents with pagination
208
+ *
209
+ * This action retrieves multiple entities with filtering, sorting, and pagination.
210
+ * It maps to the IBaseService.getAll method and handles parameter validation.
211
+ *
212
+ * @param criteria - Optional filter criteria
213
+ * @param sort - Optional sorting configuration
214
+ * @param skip - Optional number of documents to skip (for pagination)
215
+ * @param limit - Optional maximum number of documents to return
216
+ * @param selectedFields - Optional space-separated list of fields to include
217
+ * @returns Array of domain entities
218
+ *
219
+ * @example
220
+ * // Get active users sorted by creation date
221
+ * const users = await broker.call('users.getAll', {
222
+ * criteria: { active: true },
223
+ * sort: { createdAt: -1 },
224
+ * skip: 0,
225
+ * limit: 10
226
+ * });
227
+ */
228
+ [BaseServiceCommands.GetAll]: {
229
+ params: {
230
+ criteria?: Record<string, any>;
231
+ sort?: Record<string, 1 | -1>;
232
+ skip?: number;
233
+ limit?: number;
234
+ selectedFields?: string;
235
+ };
236
+ };
237
+
238
+ /**
239
+ * Get documents with pagination and total count
240
+ *
241
+ * This action retrieves multiple entities with filtering, sorting, pagination,
242
+ * and includes the total count of matching documents (useful for UI pagination).
243
+ * It maps to the IBaseService.getAllWithCount method and handles parameter validation.
244
+ *
245
+ * @param criteria - Optional filter criteria
246
+ * @param sort - Optional sorting configuration
247
+ * @param skip - Optional number of documents to skip (for pagination)
248
+ * @param limit - Optional maximum number of documents to return
249
+ * @param selectedFields - Optional space-separated list of fields to include
250
+ * @returns Object with data array and total count
251
+ *
252
+ * @example
253
+ * // Get paginated users with total count for UI pagination
254
+ * const { data, totalCount } = await broker.call('users.getAllWithCount', {
255
+ * criteria: { role: 'user' },
256
+ * skip: 20,
257
+ * limit: 10,
258
+ * sort: { createdAt: -1 }
259
+ * });
260
+ */
261
+ [BaseServiceCommands.GetAllWithCount]: {
262
+ params: {
263
+ criteria?: Record<string, any>;
264
+ sort?: Record<string, 1 | -1>;
265
+ skip?: number;
266
+ limit?: number;
267
+ selectedFields?: string;
268
+ };
269
+ };
270
+
271
+ /**
272
+ * Create or update a document
273
+ *
274
+ * This action creates a new entity or updates an existing one if an ID is provided.
275
+ * It maps to the IBaseService.insert method and handles parameter validation.
276
+ * It also emits an event for the created or updated entity if configured.
277
+ *
278
+ * @param data - Data object for the new or updated document
279
+ * @param overwrite - Optional flag to completely replace the document instead of merging
280
+ * @returns The created or updated domain entity
281
+ *
282
+ * @example
283
+ * // Create a new product
284
+ * const newProduct = await broker.call('products.insert', {
285
+ * data: { name: 'New Product', price: 99.99 }
286
+ * });
287
+ *
288
+ * // Update an existing product
289
+ * const updatedProduct = await broker.call('products.insert', {
290
+ * data: { id: 'product123', name: 'Updated Product', price: 129.99 }
291
+ * });
292
+ */
293
+ [BaseServiceCommands.Insert]: {
294
+ params: {
295
+ data: any & { id?: string };
296
+ overwrite?: boolean;
297
+ };
298
+ };
299
+
300
+ /**
301
+ * Update a document by ID
302
+ *
303
+ * This action updates an existing entity by its unique identifier.
304
+ * It maps to the IBaseService.update method and handles parameter validation.
305
+ * It also emits an event for the updated entity if configured.
306
+ *
307
+ * @param id - The unique identifier of the document to update
308
+ * @param data - Data object with fields to update
309
+ * @param overwrite - Optional flag to completely replace the document instead of merging
310
+ * @returns The updated domain entity
311
+ *
312
+ * @example
313
+ * // Update a user's role
314
+ * const updatedUser = await broker.call('users.update', {
315
+ * id: 'user123',
316
+ * data: { role: 'admin', active: true }
317
+ * });
318
+ *
319
+ * // Replace a user document entirely
320
+ * const replacedUser = await broker.call('users.update', {
321
+ * id: 'user123',
322
+ * data: { name: 'New Name', email: 'new@example.com', role: 'user' },
323
+ * overwrite: true
324
+ * });
325
+ */
326
+ [BaseServiceCommands.Update]: {
327
+ params: {
328
+ id: string;
329
+ data: any;
330
+ overwrite?: boolean;
331
+ };
332
+ };
333
+
334
+ /**
335
+ * Delete multiple documents matching criteria
336
+ *
337
+ * This action deletes multiple entities matching the provided filter criteria.
338
+ * It maps to the IBaseService.delete method and handles parameter validation.
339
+ * It also emits an event with the deletion criteria if configured.
340
+ *
341
+ * @param criteria - Filter criteria to find documents to delete
342
+ * @returns Number of deleted documents
343
+ *
344
+ * @example
345
+ * // Delete all inactive users
346
+ * const deletedCount = await broker.call('users.deleteMany', {
347
+ * criteria: { active: false }
348
+ * });
349
+ *
350
+ * // Delete users with a specific role
351
+ * const deletedCount = await broker.call('users.deleteMany', {
352
+ * criteria: { role: 'guest' }
353
+ * });
354
+ */
355
+ [BaseServiceCommands.DeleteMany]: {
356
+ params: {
357
+ criteria: Record<string, any>;
358
+ };
359
+ };
360
+ }
361
+
362
+ /**
363
+ * Interface describing the events emitted by the BaseServiceMixin
364
+ * Events follow a consistent naming pattern: `${serviceName}.on${CommandName}`
365
+ */
366
+ export interface IBaseServiceMixinEvents {
367
+ /**
368
+ * Event emitted when a document is created
369
+ * Event name format: `${serviceName}.onCreate`
370
+ * Payload: The created domain entity
371
+ *
372
+ * @example
373
+ * // Listen for user creation events
374
+ * broker.createService({
375
+ * name: 'notification',
376
+ * events: {
377
+ * 'users.onCreate': function(payload) {
378
+ * // Send welcome email to new user
379
+ * this.sendWelcomeEmail(payload.email);
380
+ * }
381
+ * }
382
+ * });
383
+ */
384
+ onCreate: any;
385
+
386
+ /**
387
+ * Event emitted when multiple documents are created
388
+ * Event name format: `${serviceName}.onBulkCreate`
389
+ * Payload: Array of created domain entities
390
+ *
391
+ * @example
392
+ * // Listen for bulk user creation events
393
+ * broker.createService({
394
+ * name: 'notification',
395
+ * events: {
396
+ * 'users.onBulkCreate': function(payload) {
397
+ * // Send welcome emails to all new users
398
+ * payload.forEach(user => this.sendWelcomeEmail(user.email));
399
+ * }
400
+ * }
401
+ * });
402
+ */
403
+ onBulkCreate: any[];
404
+
405
+ /**
406
+ * Event emitted when a document is updated
407
+ * Event name format: `${serviceName}.onUpdate`
408
+ * Payload: The updated domain entity
409
+ *
410
+ * @example
411
+ * // Listen for user update events
412
+ * broker.createService({
413
+ * name: 'audit',
414
+ * events: {
415
+ * 'users.onUpdate': function(payload) {
416
+ * // Log user update for audit trail
417
+ * this.logAuditEvent('user_updated', payload.id);
418
+ * }
419
+ * }
420
+ * });
421
+ */
422
+ onUpdate: any;
423
+
424
+ /**
425
+ * Event emitted when a document is deleted
426
+ * Event name format: `${serviceName}.onDelete`
427
+ * Payload: Object containing the ID of the deleted entity
428
+ *
429
+ * @example
430
+ * // Listen for user deletion events
431
+ * broker.createService({
432
+ * name: 'cleanup',
433
+ * events: {
434
+ * 'users.onDelete': function(payload) {
435
+ * // Clean up related user data
436
+ * this.cleanupUserData(payload.id);
437
+ * }
438
+ * }
439
+ * });
440
+ */
441
+ onDelete: { id: string };
442
+
443
+ /**
444
+ * Event emitted when multiple documents are deleted
445
+ * Event name format: `${serviceName}.onDeleteMany`
446
+ * Payload: The criteria used for deletion
447
+ *
448
+ * @example
449
+ * // Listen for bulk user deletion events
450
+ * broker.createService({
451
+ * name: 'cleanup',
452
+ * events: {
453
+ * 'users.onDeleteMany': function(payload) {
454
+ * // Clean up related data for deleted users
455
+ * this.cleanupBulkUserData(payload.criteria);
456
+ * }
457
+ * }
458
+ * });
459
+ */
460
+ onDeleteMany: { criteria: Record<string, any> };
461
+ }
@@ -0,0 +1,211 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+ /**
3
+ * @file IDataLoader.ts
4
+ * @description Defines interfaces for implementing the DataLoader pattern to efficiently batch and cache database queries.
5
+ *
6
+ * The DataLoader pattern helps prevent the N+1 query problem by consolidating multiple individual requests
7
+ * into a single batch request. This significantly improves performance for nested queries and relationship
8
+ * traversal in GraphQL or REST APIs.
9
+ *
10
+ * Architecture Context:
11
+ * - Part of the data access optimization layer
12
+ * - Sits between repositories and services/resolvers
13
+ * - Implements request batching and caching for efficient data access
14
+ * - Particularly valuable for GraphQL implementations to solve the N+1 query problem
15
+ * - Works with MongoDB collections through Mongoose
16
+ *
17
+ * Key features:
18
+ * - Request batching: Combines multiple individual requests into a single database query
19
+ * - In-memory caching: Prevents duplicate requests for the same entity
20
+ * - Custom loading options: Supports flexible query criteria beyond simple ID lookups
21
+ * - Clear cache methods: Allows for cache invalidation when data changes
22
+ *
23
+ * Usage Patterns:
24
+ * - Create a DataLoader instance for each entity type in your application
25
+ * - Use the load method for single entity retrieval by ID
26
+ * - Use loadMany for retrieving multiple entities by their IDs
27
+ * - Use withOptions for more complex queries with custom criteria
28
+ * - Clear cache entries when entities are modified
29
+ *
30
+ * Performance Impact:
31
+ * - Reduces database round trips by batching requests
32
+ * - Minimizes redundant queries through caching
33
+ * - Particularly effective for nested relationship queries in GraphQL
34
+ * - Can dramatically improve response times for complex data graphs
35
+ *
36
+ * @see IBaseMongoRepository - Repository layer that provides the underlying data access
37
+ * @see AsDomainType - Type transformation for domain objects
38
+ */
39
+
40
+ import DataLoader from 'dataloader';
41
+ import type { FilterQuery } from 'mongoose';
42
+ import { AsDomainType } from './dbCommonTypes';
43
+
44
+ /**
45
+ * Options for configuring how entities are loaded through the DataLoader
46
+ *
47
+ * @property id - Unique identifier for the request (used for caching)
48
+ * @property searchKey - The field to search by (typically 'id' or another indexed field)
49
+ * @property comparator - Optional custom function to determine if an item matches the search criteria
50
+ * @property criteria - Optional additional filter criteria to apply to the query
51
+ *
52
+ * @example
53
+ * // Basic options for loading by ID
54
+ * const options: DataLoaderOptions<UserSchema> = {
55
+ * id: '123',
56
+ * searchKey: 'id'
57
+ * };
58
+ *
59
+ * // Advanced options with custom criteria
60
+ * const options: DataLoaderOptions<UserSchema> = {
61
+ * id: 'active-admins',
62
+ * searchKey: 'role',
63
+ * criteria: { active: true, role: 'admin' },
64
+ * comparator: (opt, item) => item.role === 'admin' && item.active === true
65
+ * };
66
+ */
67
+ export interface DataLoaderOptions<SchemaType> {
68
+ /** Unique identifier for the request (used for caching) */
69
+ id: string;
70
+
71
+ /** The field to search by (typically 'id' or another indexed field) */
72
+ searchKey: keyof AsDomainType<SchemaType> | string;
73
+
74
+ /** Optional custom function to determine if an item matches the search criteria */
75
+ comparator?: (option: DataLoaderOptions<SchemaType>, item: AsDomainType<SchemaType>) => boolean;
76
+
77
+ /** Optional additional filter criteria to apply to the query */
78
+ criteria?: FilterQuery<SchemaType>;
79
+
80
+ /** Additional custom properties can be added as needed */
81
+ [key: string]: any;
82
+ }
83
+
84
+ /**
85
+ * Interface for implementing the DataLoader pattern to efficiently batch and cache database queries
86
+ *
87
+ * The IDataLoader interface provides methods for loading single or multiple entities by ID,
88
+ * clearing cache entries, and loading entities with custom options. This implementation
89
+ * supports MongoDB collections through Mongoose.
90
+ *
91
+ * @example
92
+ * // Create a user data loader
93
+ * const userLoader = new MongoDataLoader(UserModel);
94
+ *
95
+ * // Load a user by ID (batched and cached)
96
+ * const user1 = await userLoader.load('user1');
97
+ * const user2 = await userLoader.load('user2');
98
+ *
99
+ * // Load multiple users by ID in a single batch
100
+ * const users = await userLoader.loadMany(['user3', 'user4', 'user5']);
101
+ *
102
+ * // Load users with custom criteria
103
+ * const activeAdmins = await userLoader.withOptions.load({
104
+ * id: 'active-admins',
105
+ * searchKey: 'role',
106
+ * criteria: { active: true, role: 'admin' }
107
+ * });
108
+ *
109
+ * // Clear cache when a user is updated
110
+ * userLoader.clear('user1');
111
+ */
112
+ export interface IDataLoader<SchemaType> {
113
+ /**
114
+ * Load a single entity by ID
115
+ *
116
+ * This method retrieves a single entity by its unique identifier.
117
+ * Requests are batched with other load calls in the same tick of the event loop
118
+ * and results are cached to prevent duplicate database queries.
119
+ *
120
+ * @param id - The unique identifier of the entity to load
121
+ * @returns Promise resolving to the domain entity or null if not found
122
+ *
123
+ * @example
124
+ * // Load a user by ID
125
+ * const user = await userLoader.load('user123');
126
+ *
127
+ * // Handle case where entity might not exist
128
+ * const product = await productLoader.load('product456');
129
+ * if (product) {
130
+ * // Product exists, use it
131
+ * } else {
132
+ * // Product not found
133
+ * }
134
+ */
135
+ load: (id: string) => Promise<AsDomainType<SchemaType> | null>;
136
+
137
+ /**
138
+ * Load multiple entities by their IDs
139
+ *
140
+ * This method retrieves multiple entities by their unique identifiers in a single batch.
141
+ * Results are cached to prevent duplicate database queries.
142
+ *
143
+ * @param ids - Array of unique identifiers of the entities to load
144
+ * @returns Promise resolving to an array of domain entities, nulls, or errors
145
+ *
146
+ * @example
147
+ * // Load multiple users by their IDs
148
+ * const users = await userLoader.loadMany(['user1', 'user2', 'user3']);
149
+ *
150
+ * // Handle results which may include nulls for not found entities
151
+ * const validUsers = users.filter(user => user !== null && !(user instanceof Error));
152
+ */
153
+ loadMany: (ids: string[]) => Promise<(AsDomainType<SchemaType> | null | Error)[]>;
154
+
155
+ /**
156
+ * Clear a single entity from the cache
157
+ *
158
+ * This method removes a specific entity from the cache, forcing the next load
159
+ * request for this ID to query the database. Use this when an entity is updated
160
+ * or deleted to ensure fresh data is loaded.
161
+ *
162
+ * @param id - The unique identifier of the entity to remove from cache
163
+ * @returns The DataLoader instance for method chaining
164
+ *
165
+ * @example
166
+ * // Update a user and clear the cache
167
+ * await userService.update('user123', { name: 'New Name' });
168
+ * userLoader.clear('user123');
169
+ */
170
+ clear: (id: string) => DataLoader<string, AsDomainType<SchemaType> | null>;
171
+
172
+ /**
173
+ * Clear all entities from the cache
174
+ *
175
+ * This method removes all entities from the cache, forcing the next load
176
+ * requests to query the database. Use this when multiple entities are updated
177
+ * or when you need to ensure all data is fresh.
178
+ *
179
+ * @returns The DataLoader instance for method chaining
180
+ *
181
+ * @example
182
+ * // After a bulk update operation, clear the entire cache
183
+ * await userService.bulkUpdate({ role: 'user' }, { active: true });
184
+ * userLoader.clearAll();
185
+ */
186
+ clearAll: () => DataLoader<string, AsDomainType<SchemaType> | null>;
187
+
188
+ /**
189
+ * DataLoader for loading entities with custom options
190
+ *
191
+ * This property provides a DataLoader instance that supports more complex
192
+ * loading scenarios beyond simple ID lookups. Use this when you need to
193
+ * load entities based on other criteria or with additional filtering.
194
+ *
195
+ * @example
196
+ * // Load active users with the admin role
197
+ * const adminUsers = await userLoader.withOptions.load({
198
+ * id: 'active-admins', // Unique cache key
199
+ * searchKey: 'role',
200
+ * criteria: { active: true, role: 'admin' }
201
+ * });
202
+ *
203
+ * // Load products in a specific price range
204
+ * const affordableProducts = await productLoader.withOptions.load({
205
+ * id: 'affordable-products',
206
+ * searchKey: 'price',
207
+ * criteria: { price: { $lt: 100 } }
208
+ * });
209
+ */
210
+ withOptions: DataLoader<DataLoaderOptions<SchemaType>, AsDomainType<SchemaType>[]>;
211
+ }