Skip to content

Instantly share code, notes, and snippets.

@anchan828
Last active April 1, 2025 14:23
Show Gist options
  • Save anchan828/9e569f076e7bc18daf21c652f7c3d012 to your computer and use it in GitHub Desktop.
Save anchan828/9e569f076e7bc18daf21c652f7c3d012 to your computer and use it in GitHub Desktop.
This is an improvement to allow @nestjs/[email protected] to handle CustomRepository. I won't explain it specifically, but it will help in some way. https://github.com/nestjs/typeorm/pull/1233

You need to provide some classes and decorators yourself to maintain the same style as [email protected].

1. EntityRepository -> CustomRepository

@EntityRepository(UserEntity)
export class UserRepository extends Repository<UserEntity> {}

@CustomRepository(UserEntity)
export class UserRepository extends Repository<UserEntity> {}

2. forFeature -> forCustomRepository

@Module({
  exports: [UserService],
  imports: [TypeOrmModule.forFeature([UserRepository])],
  providers: [UserService],
})
export class UserModule {}

@Module({
  exports: [UserService],
  imports: [TypeOrmExModule.forCustomRepository([UserRepository])],
  providers: [UserService],
})
export class UserModule {}
@Injectable()
export class DatabaseOptions implements TypeOrmOptionsFactory {
public createTypeOrmOptions(): TypeOrmModuleOptions {
return {
...
entities: [UserEntity],
};
}
}
import { SetMetadata } from "@nestjs/common";
export const TYPEORM_EX_CUSTOM_REPOSITORY = "TYPEORM_EX_CUSTOM_REPOSITORY";
export function CustomRepository(entity: Function): ClassDecorator {
return SetMetadata(TYPEORM_EX_CUSTOM_REPOSITORY, entity);
}
import { DynamicModule, Provider } from "@nestjs/common";
import { getDataSourceToken } from "@nestjs/typeorm";
import { DataSource } from "typeorm";
import { TYPEORM_EX_CUSTOM_REPOSITORY } from "./typeorm-ex.decorator";
export class TypeOrmExModule {
public static forCustomRepository<T extends new (...args: any[]) => any>(repositories: T[]): DynamicModule {
const providers: Provider[] = [];
for (const repository of repositories) {
const entity = Reflect.getMetadata(TYPEORM_EX_CUSTOM_REPOSITORY, repository);
if (!entity) {
continue;
}
providers.push({
inject: [getDataSourceToken()],
provide: repository,
useFactory: (dataSource: DataSource): typeof repository => {
const baseRepository = dataSource.getRepository<any>(entity);
return new repository(baseRepository.target, baseRepository.manager, baseRepository.queryRunner);
},
});
}
return {
exports: providers,
module: TypeOrmExModule,
providers,
};
}
}
import { Module } from "@nestjs/common";
import { TypeOrmExModule } from "../database/typeorm-ex.module";
import { UserRepository } from "./user.repository";
import { UserService } from "./user.service";
@Module({
exports: [UserService],
imports: [TypeOrmExModule.forCustomRepository([UserRepository])],
providers: [UserService],
})
export class UserModule {}
import { Repository } from "typeorm";
import { CustomRepository } from "../database/typeorm-ex.decorator";
import { UserEntity } from "./user.entity";
@CustomRepository(UserEntity)
export class UserRepository extends Repository<UserEntity> {}
import { Injectable } from "@nestjs/common";
import { UserRepository } from "./user.repository";
@Injectable()
export class UserService {
constructor(private readonly repository: UserRepository) {}
}
@tiagoblackcode
Copy link

In case you wish to use this within a transaction you can do something like:

this.entityManager.transaction((entityManager) => {
  const myRepository = entityManager.withRepository(this.myRepository);
  myRepository.find(...);
});

@soroushmadani
Copy link

Hi, thanks for your great workaround.
I'm new in nestJs. The thing is now I can't use this service inside another module.

I have a UsersModule that uses custom repository and exports UsersService . In my AuthModule I have imported UsersModule and in AuthService I'm trying to use UsersService. I'm facing the error bellow:

Nest can't resolve dependencies of the UsersService (?). Please make sure that the argument UserRepository at index [0] is available in the UsersModule context.

any help would be appreciated.

@ricbermo
Copy link

I just found a way that, IMHO, it's cleaner and easier to use/test https://github.com/leosuncin/nest-typeorm-custom-repository

Thanks for sharing, tho.

@khasanovdiyor
Copy link

@anchan828 Hi, this helped me a lot thank you, but how can mock this in tests? it's not a provider and the way I usually do was getRepositoryToken(Model) and it doesn't work.

@anchan828
Copy link
Author

@khasanovdiyor If you have a UserRepository, you can use { provide: UserRepository, useValue: createMock() }.

@CustomRepository(UserEntity)
export class UserRepository extends Repository<UserEntity> {}

@khasanovdiyor
Copy link

@anchan828 hey thanks for the reply, yeah I was using it like that, but wasn't working, but I figured out that problem was when getting the instance with module.get(), anyways thank you!

@ffind
Copy link

ffind commented Aug 16, 2022

Hi @anchan828, I'm new in nest, what DatabaseOptions is for?

@zamanzamzz
Copy link

@anchan828 Thanks for this, I found it very helpful! It saved me quite a bit of refactoring time.

@ffind DatabaseOptions can be used in the following away in your root app module:

TypeOrmModule.forRootAsync({
  useClass: DatabaseOptions,
});

You can take a look at the documentation here: https://docs.nestjs.com/techniques/database#async-configuration

@Siusarna
Copy link

Hey everyone. Any idea how to use method from CustomRepository1 in CustomRepository2 ? I tried to inject CustomRepository1 in constructor, but then I should call super() and pass the params which I don't know :(

@anchan828
Copy link
Author

@Siusarna If you adopt my method, you cannot do Inject because CustomRepository simply creates an instance from a Class. You must manually create a Repository from the datasource.

I don't know if it actually works, but this is what it looks like.

@EntityCustomRepository(UserEntity)
export class UserRepository extends Repository<UserEntity> {

  customRepository2: CustomRepository2;

  constructor(target: EntityTarget<UserEntity>, manager: EntityManager, queryRunner?: QueryRunner) {
    super(target, manager, queryRunner);

    const baseRepository = manager.getRepository<any>(CustomEntity2);
    this.customRepository2 = new CustomRepository2(baseRepository.target, baseRepository.manager, baseRepository.queryRunner);
  }
}

@Siusarna
Copy link

Thanks a lot ❤️. I will try it out

@Rainson12
Copy link

any idea on how to make use of custom repositories when working with transactions using

const queryRunner = this.dataSource.createQueryRunner();
await queryRunner.connect();
await queryRunner.startTransaction();

and then pass queryRunner to the service where we normally would do:
const userRepository: UserRepository = queryRunner.manager.getCustomRepository(UserRepository)

however by now using customRepositories from this github thread the queryRunner no longer holds any instances of the repositories and thus throwing the error:
Custom repository UserRepository was not found. Did you forgot to put @EntityRepository decorator on it?

@tiagoblackcode
Copy link

any idea on how to make use of custom repositories when working with transactions using

const queryRunner = this.dataSource.createQueryRunner();
await queryRunner.connect();
await queryRunner.startTransaction();

and then pass queryRunner to the service where we normally would do: const userRepository: UserRepository = queryRunner.manager.getCustomRepository(UserRepository)

however by now using customRepositories from this github thread the queryRunner no longer holds any instances of the repositories and thus throwing the error: Custom repository UserRepository was not found. Did you forgot to put @EntityRepository decorator on it?

Can you do something like this?

@Rainson12
Copy link

Rainson12 commented Sep 6, 2022

any idea on how to make use of custom repositories when working with transactions using

const queryRunner = this.dataSource.createQueryRunner();
await queryRunner.connect();
await queryRunner.startTransaction();

and then pass queryRunner to the service where we normally would do: const userRepository: UserRepository = queryRunner.manager.getCustomRepository(UserRepository)
however by now using customRepositories from this github thread the queryRunner no longer holds any instances of the repositories and thus throwing the error: Custom repository UserRepository was not found. Did you forgot to put @EntityRepository decorator on it?

Can you do something like this?

apparently not since the queryRunner is created using annotation like:

@Injectable()
export class TransactionInterceptor implements NestInterceptor {
  constructor(private readonly dataSource: DataSource) {}

  public async intercept(context: ExecutionContext, next: CallHandler): Promise<Observable<any>> {
    const httpContext: HttpArgumentsHost = context.switchToHttp()
    const queryRunner: QueryRunner = this.dataSource.createQueryRunner()
    const request: RequestWithTransactionQueryRunner = httpContext.getRequest()
    request.queryRunner = queryRunner
    await request.queryRunner.connect()
    await request.queryRunner.startTransaction()
    return next.handle().pipe(
      finalize(async () => {
        await queryRunner.commitTransaction()
        await queryRunner.release()
      }),
      catchError(async error => {
        await queryRunner.rollbackTransaction()
        throw error
      })
    )
  }
}


@tiagoblackcode
Copy link

tiagoblackcode commented Sep 6, 2022

How about

queryRunner.manager.withRepository(this.myCustomRepository)

where this.myCustomRepository is the injected custom repository? I was able to use that strategy successfully. The EntityManager#withRepository method re-creates the repository and assigns both the EntityManager and the QueryRunner instances to it.

Note: I'm using this strategy with the entityManager.transaction((em) => ...) method so I haven't tested with the QueryRunner#manager approach.

@Rainson12
Copy link

How about

queryRunner.manager.withRepository(this.myCustomRepository)

where this.myCustomRepository is the injected custom repository? I was able to use that strategy successfully. The EntityManager#withRepository method re-creates the repository and assigns both the EntityManager and the QueryRunner instances to it.

Note: I'm using this strategy with the entityManager.transaction((em) => ...) method so I haven't tested with the QueryRunner#manager approach.

great Idea, didnt know about it, it does in fact work. Thanks!

@ab-etemadi
Copy link

ab-etemadi commented Sep 13, 2022

Hi everyone, any idea on How can I use QueryBuilder in user.repository.ts too .

@foliveirafilho
Copy link

Hey everyone. If you want to use transactions in a simpler way, there's a pretty cool lib that uses cls-hooked for that:

But we have to modify how custom repositories are provided in typeorm-ex.module.ts as suggested by the author of the lib:

Hope this can be helpful to someone. It worked great for me!

@rhutchison
Copy link

I created a utility method to provide custom repositories. Open to feedback: https://gist.github.com/rhutchison/a530d89c37f1978a48dcee4bf2418cb7

@vnxdev
Copy link

vnxdev commented Sep 29, 2022

import { Injectable } from '@nestjs/common';
import { DataSource, Repository } from 'typeorm';
import { User } from './user.entity';

@Injectable()
export class UserRepository extends Repository<User> {
  constructor(private readonly dataSource: DataSource) {
    super(User, dataSource.manager);
  }
}

Should do the trick. Don't forget to add UserRepository as a provider of the module.

@AntoineMaitre
Copy link

Hello, thanks for your solution, works great to bridge from nestjs v8.x / typeorm 0.2.x to nestjs 9.x / typeorm 0.3.x, however, how do you deal with custom repositories imports / mocks within Unit tests / e2e ?

Thanks in advance fo any insight

@GaryHebert
Copy link

GaryHebert commented Oct 25, 2022

Thank you for this solution for managing custom repos.

I have custom repositories and entities in a library, that I install using npm.

Datasources are defined in the client project, so from the client I name the DataSource, and use the name as parameter of TypeOrmExModule.forCustomRepository.

But I get an error saying Nest can't resolve dependencies of the MyEntityRepository (?). Please make sure that the argument DataSource at index [0] is available in the TypeOrmExModule context.

This MyEntityRepository does not exist, but MyEntity and MyRepository do.

Is there a way to fix this?

I tried to add DataSource to list of providers, but then I get:

TypeError: Cannot read properties of undefined (reading 'name') at new DataSource (D:\xxx\src\data-source\DataSource.ts:133:29) at Injector.instantiateClass (D:\xxx\node_modules\@nestjs\core\injector\injector.js:340:19) at callback (D:\xxx\node_modules\@nestjs\core\injector\injector.js:53:45) at Injector.resolveConstructorParams (D:\xxx\node_modules\@nestjs\core\injector\injector.js:132:24) at Injector.loadInstance (D:\xxx\node_modules\@nestjs\core\injector\injector.js:57:13) at Injector.loadProvider (D:\xxx\node_modules\@nestjs\core\injector\injector.js:84:9) at async Promise.all (index 3) at InstanceLoader.createInstancesOfProviders (D:\xxx\node_modules\@nestjs\core\injector\instance-loader.js:47:9) at D:\xxx\node_modules\@nestjs\core\injector\instance-loader.js:32:13 at async Promise.all (index 6)

Thanks a lot.

@godtaehee
Copy link

@GaryHebert What do you mean??? You mean that you import like this???:

TypeOrmExModule.forCustomRepository([DataSource])

@godtaehee
Copy link

@anchan828 Hey, Thanks for your answer. I use it and works perfect.

@GaryHebert
Copy link

@GaryHebert What do you mean??? You mean that you import like this???:

TypeOrmExModule.forCustomRepository([DataSource])

I modified the TypeOrmExModule like @anchan828 suggestion on june 10th, adding the dataSourceName parameter.

@lucaslgr
Copy link

lucaslgr commented Dec 7, 2022

Firstly, thanks for the gist @anchan828

I don't know nothing about nest js yet and I need to make this update. I did everything but in my service I was injecting the repository with this decorator @InjectRepository as below:

@Injectable()
export class ABCService {

  constructor(
    @InjectRepository(ABCRepository, 'FDM')
    private readonly abcRepository: ABCRepository
  ) {}
.
.
.
}

And after the modifications I'm getting this error:
Nest can't resolve dependencies of the ABCService (?). Please make sure that the argument ABC_ABCRepository at index [0] is available in the ABCModule context.

How can i fix this?
I've tried to manually construct the repository inside the service constructor, but, I don't have any of the necessary arguments there, as EntityManager and QueryRunner.

@darrenm-peppy
Copy link

import { Injectable } from '@nestjs/common';
import { DataSource, Repository } from 'typeorm';
import { User } from './user.entity';

@Injectable()
export class UserRepository extends Repository<User> {
  constructor(private readonly dataSource: DataSource) {
    super(User, dataSource.manager);
  }
}

Should do the trick. Don't forget to add UserRepository as a provider of the module.

This will work for some use cases. But it wont work if you're using transactions, because the repository constructor is called directly inside withRepository().

@darrenm-peppy
Copy link

Unfortunately, the simpler solution (as recommended by @kamilmysliwiec here nestjs/typeorm#1422) does not work with transactions.

@happytalkKO
Copy link

Any idea for multiple database connection with custom repositories?

@hamza-HL
Copy link

import { Injectable } from '@nestjs/common';
import { DataSource, Repository } from 'typeorm';
import { User } from './user.entity';

@Injectable()
export class UserRepository extends Repository<User> {
  constructor(private readonly dataSource: DataSource) {
    super(User, dataSource.manager);
  }
}

Should do the trick. Don't forget to add UserRepository as a provider of the module.

It works for me, thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment