This is my implementation:

Base repository:

export class Repository<T> {
  repository: Model<T>;

  constructor(
    repository: Model<T, Record<string, never>, Record<string, never>>,
  ) {
    this.repository = repository;
  }

  async find(filter: FilterQuery<T | any>, projection?: any): Promise<T[]> {
    return this.repository.find(filter, projection);
  }

  async findById(
    id: string,
    projection?: ProjectionType<T>,
    options?: QueryOptions<T>,
  ): Promise<T> {
    return this.repository.findById(id, projection, options);
  }

  async findOne(
    filter?: FilterQuery<T>,
    projection?: ProjectionType<T>,
    options?: QueryOptions<T>,
  ): Promise<T> {
    return this.repository.findOne(filter, projection, options);
  }

  async create(doc: T | AnyObject | DocumentDefinition<T>): Promise<T> {
    return this.repository.create(doc);
  }
  .
  .
  .
}

Entity repository:

export default class UserRepository extends Repository<UserDocument> {
  constructor(@InjectModel(User.name) private userModel: Model<UserDocument>) {
    super(userModel);
  }

  async findByEmail(email: string): Promise<UserDocument> {
    return this.findOne({ email });
  }
}

Service:

export default class UserService {
  constructor(private readonly userRepository: UserRepository) {}

  async createUser(email: string, password: string): Promise<UserDocument> {
    const user = await this.userRepository.findByEmail(email);

    if (user) {
      throw new UserAlreadyExistsException();
    }

    const createdUser = await this.userRepository.create({ email, password });

    return createdUser;
  }
}

User module:

@Module({
  imports: [
    MongooseModule.forFeature([{ name: User.name, schema: UserSchema }]),
  ],
  controllers: [],
  providers: [UserRepository, UserService],
  exports: [UserRepository, UserService],
})
export default class UserModule {}

Mongo schema:

import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
import { HydratedDocument } from 'mongoose';

export type UserDocument = HydratedDocument<User>;

@Schema({
  versionKey: false,
  timestamps: true,
  toJSON: {
    virtuals: true,
    transform: (doc, ret) => {
      delete ret._id;
    },
  },
})
export class User {
  @Prop({
    type: String,
    required: true,
  })
  email: string;

  @Prop({
    type: String,
    required: true,
  })
  password: string;
}

export const UserSchema = SchemaFactory.createForClass(User);
Answer from avnigenc on Stack Overflow
๐ŸŒ
Better Programming
betterprogramming.pub โ€บ implementing-a-generic-repository-pattern-using-nestjs-fb4db1b61cce
Implementing a Generic Repository Pattern Using NestJS | by Royi Benita, Senior Full Stack Developer At Armis | Better Programming
January 27, 2022 - Implementing a Generic Repository Pattern Using NestJS Generic Repository โ€” A NestJS and Mongoose implementation Repositories are classes or components that encapsulate the logic required to access โ€ฆ
๐ŸŒ
Medium
medium.com โ€บ @alvaronicoli โ€บ generic-repository-pattern-in-typescript-with-nestjs-4793e7a551a5
Generic Repository Pattern in Typescript with NestJS | by Alvaro Nicoli | Medium
March 28, 2024 - @Injectable() export class CatsService { // NestJS docs: // constructor(@InjectModel(Cat.name) private catModel: Model<Cat>) {} // Our goal constructor(@InjectRepository(Cat.name) private catRepository: Repository<Cat>) {} }
๐ŸŒ
GitHub
github.com โ€บ mikemajesty โ€บ nestjs-mongoose-generic-repository
GitHub - mikemajesty/nestjs-mongoose-generic-repository: Nestjs mongoose generic repository
--repository.ts import { Injectable } from '@nestjs/common'; import { InjectModel } from '@nestjs/mongoose'; import { Model } from 'mongoose'; import { Repository } from 'nestjs-mongoose-generic-repository'; import { CatDocument, Cats } from './schema'; @Injectable() export class CatsRepository extends Repository<CatDocument> { constructor(@InjectModel(Cats.name) private entity: Model<CatDocument>) { super(entity); } }
Starred by 14 users
Forked by 3 users
Languages ย  TypeScript 100.0% | TypeScript 100.0%
Top answer
1 of 1
1

This is my implementation:

Base repository:

export class Repository<T> {
  repository: Model<T>;

  constructor(
    repository: Model<T, Record<string, never>, Record<string, never>>,
  ) {
    this.repository = repository;
  }

  async find(filter: FilterQuery<T | any>, projection?: any): Promise<T[]> {
    return this.repository.find(filter, projection);
  }

  async findById(
    id: string,
    projection?: ProjectionType<T>,
    options?: QueryOptions<T>,
  ): Promise<T> {
    return this.repository.findById(id, projection, options);
  }

  async findOne(
    filter?: FilterQuery<T>,
    projection?: ProjectionType<T>,
    options?: QueryOptions<T>,
  ): Promise<T> {
    return this.repository.findOne(filter, projection, options);
  }

  async create(doc: T | AnyObject | DocumentDefinition<T>): Promise<T> {
    return this.repository.create(doc);
  }
  .
  .
  .
}

Entity repository:

export default class UserRepository extends Repository<UserDocument> {
  constructor(@InjectModel(User.name) private userModel: Model<UserDocument>) {
    super(userModel);
  }

  async findByEmail(email: string): Promise<UserDocument> {
    return this.findOne({ email });
  }
}

Service:

export default class UserService {
  constructor(private readonly userRepository: UserRepository) {}

  async createUser(email: string, password: string): Promise<UserDocument> {
    const user = await this.userRepository.findByEmail(email);

    if (user) {
      throw new UserAlreadyExistsException();
    }

    const createdUser = await this.userRepository.create({ email, password });

    return createdUser;
  }
}

User module:

@Module({
  imports: [
    MongooseModule.forFeature([{ name: User.name, schema: UserSchema }]),
  ],
  controllers: [],
  providers: [UserRepository, UserService],
  exports: [UserRepository, UserService],
})
export default class UserModule {}

Mongo schema:

import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
import { HydratedDocument } from 'mongoose';

export type UserDocument = HydratedDocument<User>;

@Schema({
  versionKey: false,
  timestamps: true,
  toJSON: {
    virtuals: true,
    transform: (doc, ret) => {
      delete ret._id;
    },
  },
})
export class User {
  @Prop({
    type: String,
    required: true,
  })
  email: string;

  @Prop({
    type: String,
    required: true,
  })
  password: string;
}

export const UserSchema = SchemaFactory.createForClass(User);
๐ŸŒ
GitHub
github.com โ€บ xavism โ€บ nestjs-generic-crud
GitHub - xavism/nestjs-generic-crud: This repository is basically a NestJS API that provides an easy way to create a basic CRUD ยท GitHub
This repository is basically a NestJS API that provides an easy way to create a basic CRUD - xavism/nestjs-generic-crud
Starred by 66 users
Forked by 19 users
Languages ย  TypeScript 99.0% | JavaScript 1.0%
๐ŸŒ
DEV Community
dev.to โ€บ imzihad21 โ€บ a-generic-repository-pattern-for-nestjs-with-mongoose-for-mongodb-gp3
A Generic Repository Pattern for NestJS with Mongoose for MongoDB - DEV Community
April 5, 2025 - 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.con
๐ŸŒ
DEV Community
dev.to โ€บ josuto โ€บ lightweight-generic-repository-for-nestjs-38fi
Lightweight Abstract Repository for NestJS - DEV Community
October 15, 2023 - In this article, I will explain how to develop a sample application using NestJS and MongoDB, leveraging the functionalities provided by the monguito, a lightweight and type-safe library to seamlessly create custom database repositories for Node.js applications.
๐ŸŒ
CHUKWUEMEKE CLINTON
chukwuemekeclinton.hashnode.dev โ€บ enhancing-code-reusability-with-nestjs-abstract-repository-pattern-best-practices-and-examples
Enhancing Code Reusability with NestJS Abstract Repository Pattern Best Practices and Examples
September 1, 2023 - By using the abstract repository pattern in NestJS, you achieve a clear separation of concerns between the data access layer and the business logic layer. This enhances the maintainability and testability of your application, allowing you to switch out underlying data storage implementations with minimal impact on the rest of the codebase. An Abstract Repository pattern is a great way to decouple your app from external DB frameworks, and generic repositories reduce the amount of code you need to write in order to accomplish it.
Find elsewhere
๐ŸŒ
IMZihad21
imzihad21.github.io โ€บ articles โ€บ a โ€บ a-generic-repository-pattern-for-nestjs-with-mongoose-for-mongodb-gp3
A Generic Repository Pattern for NestJS with Mongoose for MongoDB | IMZihad21 | Software Engineer
November 3, 2024 - 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 ??
๐ŸŒ
GitHub
github.com โ€บ mtsluna โ€บ Generic-NestJS-And-TypeORM
GitHub - mtsluna/Generic-NestJS-And-TypeORM: Generic project made with nodeJS, with the framework NestJS and TypeORM
September 6, 2019 - Generic project made with nodeJS, with the framework NestJS and TypeORM - mtsluna/Generic-NestJS-And-TypeORM
Starred by 6 users
Forked by 4 users
Languages ย  TypeScript 100.0% | TypeScript 100.0%
๐ŸŒ
LinkedIn
linkedin.com โ€บ pulse โ€บ implementing-repository-pattern-nestjs-nadeera-sampath
Implementing Repository Pattern With NestJS
December 15, 2023 - This command will create a new NestJS application in a directory named "my-app." Navigate to the newly created directory: bash $ cd my-app $ npm install --save typeorm mysql2 ... Here's an example of a repository implementation using a relational database (e.g., MyqlSQL) with the help of an ORM like TypeORM:
๐ŸŒ
GitHub
github.com โ€บ jmj0502 โ€บ generic-typeorm-repository
GitHub - jmj0502/generic-typeorm-repository: A basic package aimed at providing a generic and extendable typeorm repository for nestjs apps.
A basic package aimed at providing a generic and extendable typeorm repository for nestjs apps. - jmj0502/generic-typeorm-repository
Author ย  jmj0502
๐ŸŒ
codepedia
gaiyaobed.hashnode.dev โ€บ building-reusable-crud-apis-with-typeorm-and-nestjs
Building Reusable CRUD APIs with TypeORM and NestJS
July 2, 2023 - In this blog post, we'll walk through how to implement a repository pattern in your NestJS project using TypeORM. We'll create a base repository class that implements common CRUD methods using the TypeORM repository, and a generic CRUD service class that uses a generic repository interface to provide common CRUD methods.
Top answer
1 of 2
1

The problem here comes from the @InjectRepository(User) : it injects an instance of Repository instead of the BaseRepository one.

I found this repository that provide a way to override the repository provided by the TypeORM module, but only for a specific entity.

However, we could adapt his approach to provide a generic repository instead :

@Module({
  imports: [TypeOrmModule.forFeature([User])],
  providers: [buildCustomRepositoryProvider<User>(User), UserService],
})
export class UserModule {
}

With a helper file like this :

import { DataSource, Repository, UpdateResult } from 'typeorm';
import { Provider } from '@nestjs/common';
import { getDataSourceToken, getRepositoryToken } from '@nestjs/typeorm';
import { EntityClassOrSchema } from '@nestjs/typeorm/dist/interfaces/entity-class-or-schema.type';

export interface BaseRepository<T> extends Repository<T> {
  this: Repository<T>;

  updateAndLog(id: string, data: any): Promise<UpdateResult>;
}

export function buildCustomRepositoryMethods<T>(): Pick<BaseRepository<T>, 'updateAndLog'> {
  return {
    async updateAndLog(id: string, data: any): Promise<UpdateResult> {
      const entity = await this.findOne({ where: { id: id as any } });
      const savedEntity = await this.update(id, data);
      // log the data here
      return savedEntity;
    },
  };
}

export function buildCustomRepositoryProvider<T>(entity: EntityClassOrSchema): Provider {
  return {
    provide: getRepositoryToken(entity),
    inject: [getDataSourceToken()],
    useFactory: (dataSource: DataSource) => {
      // Override the default repository with a custom one
      return dataSource.getRepository(entity).extend(buildCustomRepositoryMethods<T>());
    },
  };
}

Therefore, the @InjectRepository(User) will inject an instance of Repository<User> extended with the methods provided by the BaseRepository interface.

(Note : using the extend method here to create a custom repository as it's the recommended way since TypeORM 0.3, see here)

2 of 2
0

Try this soluion, I am using this solution in my application

import { Type } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { ObjectLiteral, Repository } from 'typeorm';

interface IDataService<T extends ObjectLiteral> {
  readonly repository: Repository<T>;
  findById: (id: string | number) => Promise<T | null>;
}

type Constructor<I> = new (...args: any[]) => I;

export function DataService<T extends ObjectLiteral>(
   entity: Constructor<T>,
): Type<IDataService<T>> {

   class DataServiceHost implements IDataService<T> {
     @InjectRepository(entity) public readonly repository: Repository<T>;

     public async findById(id: string | number): Promise<T | null> {
        return this.repository.findOneBy({ id } as any);
     }
   }
   return DataServiceHost;
}

Extend your service from DataService class,

class YourService extends DataService<YourDatasebaseEntity>(YourDatasebaseEntity) 
๐ŸŒ
GitHub
github.com โ€บ BrahimAbdelli โ€บ nestier
GitHub - BrahimAbdelli/nestier: A production-ready NestJS boilerplate with Hexagonal Architecture, Domain-Driven Design, Generic Repository Pattern; CRUD, TypeORM, MongoDB, JWT Authentication, Swagger, Winston Logger, Mailjet, AutoMapper, Advanced Search, Pagination, Soft Delete, Docker, Jest, ESLint, Prettier and SonarQube. ยท GitHub
A production-ready NestJS boilerplate with Hexagonal Architecture, Domain-Driven Design, Generic Repository Pattern; CRUD, TypeORM, MongoDB, JWT Authentication, Swagger, Winston Logger, Mailjet, AutoMapper, Advanced Search, Pagination, Soft Delete, Docker, Jest, ESLint, Prettier and SonarQube.
Starred by 71 users
Forked by 15 users
Languages ย  TypeScript 92.9% | JavaScript 3.6% | Handlebars 3.0% | Dockerfile 0.5%
๐ŸŒ
Medium
farizmamad.medium.com โ€บ article-reference-on-how-to-implement-repository-pattern-in-nestjs-86391e5ad7ef
Article Reference on How to Implement Repository Pattern in NestJS | by fariz mamad | Medium
March 25, 2023 - However, it was harder to find an example for NestJS. It turns out Royi Benita has written a great article about this topic. He started his article with a definition, followed by why the principle should be applied, and explained by the use case example. He also provided the link to his github repo to find out the full code. If you are interested, check his article below. https://betterprogramming.pub/implementing-a-generic-repository...
๐ŸŒ
Stack Overflow
stackoverflow.com โ€บ questions โ€บ 75866614 โ€บ inject-dynamic-entity-to-generic-repository-in-nestjs
Inject dynamic entity to Generic repository in NestJS
I have a generic class called BaseRepository which is injected in Admin module. I would like to pass dynamic entities at runtime in BaseRepository. But i am getting following error. Nest can't resolve dependencies of the BaseRepository (?, DataSource). Please make sure that the argument Object at index [0] is available in the AdminModule context. // base.repository.ts @Injectable() export class BaseRepository<T extends UserEntityInterface> implements GenericInterface<T> { constructor(modelType: ObjectType<T>, dataSource: DataSource) { this.modelType = modelType; if (dataSource !== undefined) { this.repository = dataSource.getRepository(this.modelType); } } async findAll(): Promise<T[]> { return (await this.repository.find()) as T[]; } async save(entity: T): Promise<T> { return await this.repository.save(entity); } }
๐ŸŒ
GitHub
github.com โ€บ topics โ€บ generic-repository-pattern
generic-repository-pattern ยท GitHub Topics ยท GitHub
template mongo mongodb mongoose monorepo mongoosejs mongodb-database adapter-pattern nestjs generic-repository-pattern monorepo-example monorepo-multipackage monorepo-boilerplate nestjs-auth monoreponest dependency-inversion-pattern anti-corruption-layer-pattern swaggger-documentation ...