Skip to content

Instantly share code, notes, and snippets.

@anchan828
Last active May 1, 2024 04:26
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/typeorm@8.1.x 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 typeorm@2.x.

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) {}
}
@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!

@eric-ho-github
Copy link

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.

Hi mate, I hit my head into the same issue as yours.
What was your solution for it?
thanks

@Philipinho
Copy link

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.

Hi mate, I hit my head into the same issue as yours. What was your solution for it? thanks

Add the Repository to the module providers as you would with the service.
e.g

providers: [UserService, UserRepository],

Here is my setup.

user.respository.ts

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

@Injectable()
export class UserRepository extends Repository<User> {
  constructor(private dataSource: DataSource) {
    super(User, dataSource.createEntityManager());
  }
  async findByEmail(email: string) {
    return this.findOne({ where: { email: email } });
  }
}

user.module.ts

import { Module } from '@nestjs/common';
import { UserService } from './user.service';
import { UserController } from './user.controller';
import { TypeOrmModule } from '@nestjs/typeorm';
import { User } from './entities/user.entity';
import { UserRepository } from './repositories/user.repository';

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

Now, I need it in my auth module.
auth.module.ts

@Module({
  imports: [
    TypeOrmModule.forFeature([User]),
  ],
  controllers: [AuthController],
  providers: [AuthService, TokenService, UserService, UserRepository],
})
export class AuthModule {}

Using it in my auth service.
auth.service.ts

@Injectable()
export class AuthService {
  constructor(
    private userRepository: UserRepository,
    private userService: UserService,
  ) {}

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