This analysis examines a sophisticated generic repository pattern for MongoDB integration in NestJS applications, demonstrating industry-standard practices for database abstraction, error mitigation, and type-safe query orchestration. The implementation adheres to SOLID principles while maintaining full compatibility with Mongoose's feature set.
Core Implementation
The complete unmodified repository class provides foundational CRUD operations:
import { ConflictException, Logger, NotFoundException } from "@nestjs/common"; import { ObjectId } from "mongodb"; import { Document, FilterQuery, FlattenMaps, Model, QueryOptions, SaveOptions, UpdateQuery, UpdateWithAggregationPipeline, } from "mongoose"; export class GenericRepository<T extends Document> { private readonly internalLogger: Logger; private readonly internalModel: Model<T>; constructor(model: Model<T>, logger?: Logger) { this.internalModel = model; this.internalLogger = logger || new Logger(this.constructor.name); } async create(doc: Partial<T>, saveOptions: SaveOptions = {}): Promise<T> { try { const createdEntity = new this.internalModel(doc); const savedResult = await createdEntity.save(saveOptions); return savedResult; } catch (error) { if (error?.name === "MongoServerError" && error?.code === 11000) { this.internalLogger.error("Duplicate key error while creating:", error); throw new ConflictException("Document already exists with provided inputs"); } throw error; } } async getAll( filter: FilterQuery<T> = {}, options: QueryOptions = {}, ): Promise<FlattenMaps<T>[]> { try { if (!options.sort) { options.sort = { createdAt: -1 }; } const result = await this.internalModel .find(filter, null, options) .lean() .exec(); return result; } catch (error) { this.internalLogger.error("Error finding entities:", error); return []; } } async getOneWhere( filter: FilterQuery<T>, options: QueryOptions = {}, ): Promise<T | null> { try { const result = await this.internalModel .findOne(filter, null, options) .exec(); return result; } catch (error) { this.internalLogger.error("Error finding entity by ID:", error); return null; } } async getOneById(id: string, options: QueryOptions = {}): Promise<T | null> { try { const result = await this.internalModel .findOne({ _id: id }, null, options) .exec(); return result; } catch (error) { this.internalLogger.error("Error finding entity by ID:", error); return null; } } async updateOneById( documentId: string, updated: UpdateWithAggregationPipeline | UpdateQuery<T>, options: QueryOptions = {}, ): Promise<T> { try { const result = await this.internalModel .findOneAndUpdate( { _id: documentId }, { ...updated, updatedAt: new Date() }, { ...options, new: true }, ) .exec(); if (!result) { throw new NotFoundException("Document not found with provided ID"); } return result; } catch (error) { if (error?.name === "MongoServerError" && error?.code === 11000) { this.internalLogger.error("Duplicate key error while updating:", error); throw new ConflictException("Document already exists with provided inputs"); } this.internalLogger.error("Error updating one entity:", error); throw error; } } async removeOneById(id: string): Promise<boolean> { try { const { acknowledged } = await this.internalModel .deleteOne({ _id: id }) .exec(); return acknowledged; } catch (error) { this.internalLogger.error("Error removing entities:", error); throw error; } } async count(filter: FilterQuery<T> = {}): Promise<number> { try { const count = await this.internalModel.countDocuments(filter).exec(); return count; } catch (error) { this.internalLogger.error("Error counting documents:", error); throw error; } } async validateObjectIds(listOfIds: string[] = []): Promise<boolean> { try { if (!Array.isArray(listOfIds) || !listOfIds?.length) { return false; } const objectIdStrings = listOfIds.map(String); const objectIds = objectIdStrings.map((id) => new ObjectId(id)); const result = await this.internalModel .find({ _id: { $in: objectIds } }) .select("_id") .lean() .exec(); return listOfIds.length === result?.length; } catch (error) { this.internalLogger.error("Error during validation:", error); return false; } } }
Structural Analysis
1. Type-Safe Foundation
- Generic
<T extends Document>
parameter ensures collection-specific typing - FlattenMaps return type guarantees lean document serialization
2. Error Mitigation Strategy
- Duplicate key detection (MongoServerError code 11000)
- Contextual exception conversion (ConflictException/NotFoundException)
- Fallthrough error propagation for custom handling
3. Query Optimization
- Automatic createdAt sorting in getAll
- Lean query execution for reduced memory footprint
- Selective _id projection in validateObjectIds
Operational Paradigms
-
Document Lifecycle Management
- Atomic create/update/delete operations
- Built-in timestamp management (createdAt/updatedAt)
-
Validation Protocol
- Batch ObjectID verification via $in operator
- Type coercion safeguards for ID lists
-
Audit Capabilities
- Count method for aggregate analytics
- Full query logging for operational transparency
Enterprise Implementation Scenarios
-
User Management Systems
- Conflict detection during user registration
- Secure credential updates via atomic operations
-
Inventory Control Platforms
- Bulk product ID validation for order processing
- Optimized catalog browsing with lean queries
-
Financial Transaction Logs
- Immutable audit trails through createdAt sorting
- Atomic balance updates with rollback safeguards
Architectural Benefits
-
Cross-Cutting Concerns
- Centralized logging infrastructure
- Uniform error handling patterns
-
Database Agnosticism
- Abstracted Mongoose implementation details
- Clear separation between business logic and persistence
-
Performance Considerations
- Lean document returns minimize payload size
- Batch operations reduce database roundtrips
This implementation establishes a robust foundation for enterprise-grade applications, providing:
- Type Integrity: Compile-time validation of document structures
- Operational Safety: Transaction-ready method signatures
- Diagnostic Visibility: Contextual logging throughout data operations
The pattern serves as a blueprint for complex systems requiring maintainable, scalable data access layers while maintaining full compatibility with MongoDB's native capabilities.
Top comments (0)