Search code examples
nestjstypeormrepository-patternnestjs-typeorm

NestJS / TypeORM: Custom repository method is not accessible in service


New to NestJS and TypeORM, and the similar questions on SO didn't solve my problem.

I have a custom TypeORM repository in NestJS using it in service, but it fails with error: TypeError: this.tenantRepository.createTenant is not a function.

tenants.module.ts:

import { TenantRepository } from './tenant.repository';

@Module({
  imports: [
    TypeOrmModule.forFeature([TenantRepository]),
  ],
  controllers: [TenantsController],
  providers: [TenantsService],

})
export class TenantsModule { }

tenant.repository.ts:

// ...
import { TenantEntity } from './entities/tenant.entity';

@EntityRepository(TenantEntity)
export class TenantRepository extends Repository<TenantEntity>{

    async createTenant(createTenantDto: CreateTenantDto): Promise<TenantEntity> {
        const { name, email } = createTenantDto;

        const newTenant = new TenantEntity()
        newTenant.name = name;
        newTenant.email = email;
        await newTenant.save()

        return newTenant;
    }
}

And here's where the error is triggered (tenants.service.ts)

// ...
import { TenantEntity } from './entities/tenant.entity';
import { TenantRepository } from './tenant.repository';

@Injectable()
export class TenantsService {

  constructor(
    @InjectRepository(TenantRepository)
    private tenantRepository: TenantRepository
  ) { }

  async createTenant(createTenantDto: CreateTenantDto): Promise<TenantEntity> {
    return await this.tenantRepository.createTenant(createTenantDto); // <-- ERROR

  }
}

I can inject entity in service and use it for simple CRUD, but I want to separate concerns and use the repository pattern. This is a POST endpoint and the error is only after submission from Swagger. Also, VS Code autocomplete is suggesting createTenant after typing this.tenantRepository Where am I going wrong?


Solution

  • EntityRepository decorator was deprecated, and as far as I know, you need to define a custom class that extends Repository and decorate it with @Injectable. Hence, you need to have some changes as follows:

    tenant.repository.ts:

    import { Injectable } from '@nestjs/common';
    import { DataSource, Repository } from 'typeorm';
    
    @Injectable()
    export class TenantRepository extends Repository<TenantEntity>{
    
      constructor(private dataSource: DataSource) {
        super(TenantEntity, dataSource.createEntityManager());
      }
    
      async createTenant(createTenantDto: CreateTenantDto): Promise<TenantEntity> {
        const { name, email } = createTenantDto;
    
        const newTenant = this.create({ name, email });
        await this.save(newTenant);
    
        return newTenant;
      }
    }
    

    tenants.module.ts:

    import { TenantRepository } from './tenant.repository';
    
    @Module({
      imports: [
        TypeOrmModule.forFeature([TenantRepository]),
      ],
      controllers: [TenantsController],
      providers: [TenantsService, TenantRepository],
    
    })
    export class TenantsModule { }
    

    tenants.service.ts:

    import { TenantEntity } from './entities/tenant.entity';
    import { TenantRepository } from './tenant.repository';
    
    @Injectable()
    export class TenantsService {
    
      constructor(
        private tenantRepository: TenantRepository
      ) { }
    
      async createTenant(createTenantDto: CreateTenantDto): Promise<TenantEntity> {
        return await this.tenantRepository.createTenant(createTenantDto);
      }
    }
    

    You also have access to built-in typeorm methods like save, create, find, etc. since the custom repository is derived from Repository class.