Search code examples
nestjstypeormrepository-pattern

How to create custom (separate file) repository in NestJS 9 with TypeORM 0.3.x


This is not a duplicate Q. Please don't mark this as that.

Following is not I want

import { EntityRepository, Repository } from "typeorm";
import { Test } from "./test.model";
import { Injectable } from "@nestjs/common";

@EntityRepository(Test)
export class TestRepository extends Repository<Test> {}

the @EntityRepository decorator is now deprecated.

I also don't want to make a fake repository like in here: https://stackoverflow.com/a/73352265/5420070

Don't want this either as I've to extract manager from dataSource, I don't want this because I think this is not the best way.

    export const UserRepository = dataSource.getRepository(User).extend({
        //                        ^^^^^^^^^^ from where this came from
        findByName(firstName: string, lastName: string) {
            return this.createQueryBuilder("user")
                .where("user.firstName = :firstName", { firstName })
                .andWhere("user.lastName = :lastName", { lastName })
                .getMany()
        },
    })

Found above in: https://orkhan.gitbook.io/typeorm/docs/custom-repository#how-to-create-custom-repository

I don't think this is in NestJS context.

What I want Want to know right way to make custom repository in latest version of NestJS (v9) & TypeORM (v0.3). In @EntityRepository deprecation note, they said that need to extend the repo to create custom repo like someRepo.extend({}). I want to know how to do it in NestJS way


Solution

  • In order to achieve what you want, you could do something like the following.

    This solution is inspired by the official NestJS docs related to this topic, with some customization.

    Steps to achieve it:

    1. Create your TypeOrm entity as usual, let's say UserEntity (user.entity.ts file)
    2. Create a UserRepository class (user.repository.ts file)
    3. Create a UserService class as usual (user.service.ts file)
    4. Import the UserRepository into your UserService
    5. Update UserModule in order to provide the UserRepository and needed UserEntity

    Detailed implementation example

    1. user.entity.ts file

    import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';
    
    @Entity()
    export class UserEntity {
      @PrimaryGeneratedColumn()
      id: number;
    
      @Column()
      firstName: string;
    
      @Column()
      lastName: string;
    
      @Column({ default: true })
      isActive: boolean;
    }
    

    2. user.repository.ts file

    import { InjectRepository } from '@nestjs/typeorm';
    import { Repository } from 'typeorm';
    import { UserEntity } from './user.entity';
    
    export class UserRepository extends Repository<UserEntity> {
        constructor(
            @InjectRepository(UserEntity)
            private userRepository: Repository<UserEntity>
        ) {
            super(userRepository.target, userRepository.manager, userRepository.queryRunner);
        }
    
        // sample method for demo purposes
        async findByEmail(email: string): Promise<UserEntity> {
            return await this.userRepository.findOneBy({ email }); // could also be this.findOneBy({ email });, but depending on your IDE/TS settings, could warn that userRepository is not used though. Up to you to use either of the 2 methods
        }
        
        // your other custom methods in your repo...
    }
    

    3. & 4. user.service.ts file

    import { Injectable } from '@nestjs/common';
    import { InjectRepository } from '@nestjs/typeorm';
    import { UserRepository } from './user.repository';
    import { UserEntity } from './user.entity';
    
    @Injectable()
    export class UserService {
      constructor(
        private readonly userRepository: UserRepository, // import as usual
      ) {}
    
      findAll(): Promise<UserEntity[]> {
        return this.userRepository.find();
      }
      
      // call your repo method
      findOneByEmail(email: string): Promise<UserEntity> {
        return this.userRepository.findByEmail({ email });
      }
    
      findOne(id: number): Promise<UserEntity> {
        return this.userRepository.findOneBy({ id });
      }
    
      async remove(id: string): Promise<void> {
        await this.userRepository.delete(id);
      }
      
      // your other custom methods in your service...
    }
    

    5. Updating UserModule (user.module.ts file)

    import { Module } from '@nestjs/common';
    import { TypeOrmModule } from '@nestjs/typeorm';
    import { UserService } from './user.service';
    import { UserController } from './user.controller';
    import { UserEntity } from './user.entity';
    
    @Module({
      imports: [TypeOrmModule.forFeature([UserEntity])], // here we provide the TypeOrm support as usual, specifically for our UserEntity in this case
      providers: [UserService, UserRepository], // here we provide our custom repo
      controllers: [UserController],
      exports: [UserService, UserRepository] // add this only if you use service and/or custom repo within another module/service
    })
    export class UserModule {}
    

    With this in place, you should be able to import the UserModule in your AppModule and be able to both implement custom methods in the UserRepository and use them in the UserService. You should also be able to call the manager and queryRunnner of the custom repository.

    Additional Note

    If you need to directly call your UserRepository methods from within another module/service, then update UserModule to export the UserRepository

    Hope it helps, don't hesitate to comment.