Search code examples
typescriptmongodbnestjstypeorm

How to do custom repository using TypeORM (MongoDB) in NestJS?


I have a question. With @EntityRepository decorator being marked as deprecated in typeorm@^0.3.6, what is now the recommended or TypeScript-friendly way to create a custom repository for an entity in NestJS? A custom repository before would look like this:

// users.repository.ts
import { EntityRepository, Repository } from 'typeorm';
import { User } from './user.entity';

@EntityRepository(User)
export class UsersRepository extends Repository<User> {
  async createUser(firstName: string, lastName: string): Promise<User> {
    const user = this.create({
      firstName,
      lastName,
    });

    await this.save(user);

    return user;
  }
}

And since NestJS is by default configured with TypeScript support, I will be able to call usersRepository.createUser() without an issue in a service like this:

// users.service.ts
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { User } from './user.entity';
import { UsersRepository } from './users.repository';

@Injectable()
export class UsersService {
  constructor(
    @InjectRepository(UsersRepository)
    private readonly usersRepository: UsersRepository,
  ) {}

  async createUser(firstName: string, lastName: string): Promise<User> {
    return this.usersRepository.createUser(firstName, lastName);
  }
}

This is how the modules would import the custom repository:

// users.module.ts
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { UsersController } from './users.controller';
import { UsersRepository } from './users.repository';
import { UsersService } from './users.service';

@Module({
  imports: [TypeOrmModule.forFeature([UsersRepository])],
  controllers: [UsersController],
  providers: [UsersService],
  exports: [UsersService],
})
export class UsersModule {}

Also the reason why I mentioned MongoDB here is because I tried using [email protected] where @EntityRepository is still supported but I receive an error when I tried to import it in the module stating Repository not found or something. Do note, if I chose postgresql as my database in TypeORM with the same changes above, I don't have this issue. Hence I went to check the latest only to find out it is already deprecated, I also didn't find any example in NestJS documentation.


Solution

  • I think I found a solution to this which allows to call custom methods but also inherited ones. It seems that this "issue" is not too popular yet but there is definitely some chatter about it within the typeorm GitHub threads: https://github.com/typeorm/typeorm/issues/9013

    The following solution uses MySQL as underlying database driver but I assume it will work for MongoDB as well.

    team.repository.ts

    import {DataSource, Repository} from 'typeorm';
    import {Injectable} from '@nestjs/common';
    import {Team} from '@Domain/Team/Models/team.entity';
    
    @Injectable()
    export class TeamRepository extends Repository<Team>
    {
        constructor(private dataSource: DataSource)
        {
            super(Team, dataSource.createEntityManager());
        }
    
        /**
         * Add a basic where clause to the query and return the first result.
         */
        async firstWhere(column: string, value: string | number, operator = '='): Promise<Team | undefined>
        {
            return await this.createQueryBuilder()
                             .where(`Team.${column} ${operator} :value`, {value: value})
                             .getOne();
        }
    }
    
    

    team.service.ts

    import {Injectable} from '@nestjs/common';
    import {Team} from '@Domain/Team/Models/team.entity';
    import {TeamRepository} from '@Domain/Team/Repositories/team.repository';
    
    @Injectable()
    export class TeamService
    {
        constructor(
            private teamRepository: TeamRepository,
        )
        {
        }
    
        async create(): Promise<Team>
        {
            const team: Team = await this.teamRepository.firstWhere('id', 1);
    
            return this.teamRepository.save(team);
        }
    }
    

    team.module.ts

    import {Module} from '@nestjs/common';
    import {TeamService} from '@Domain/Team/Services/team.service';
    import {TypeOrmModule} from '@nestjs/typeorm';
    import {Team} from '@Domain/Team/Models/team.entity';
    import {TeamRepository} from '@Domain/Team/Repositories/team.repository';
    
    @Module({
                imports:   [TypeOrmModule.forFeature([Team])],
                exports:   [TeamService],
                providers: [TeamService, TeamRepository],
            })
    export class TeamModule
    {
    }