NestJS实现Repository模式时TypeORM基类方法调用报错解决
我在NestJS里实现标准Repository模式:定义了IUserRepository接口,让UserRepository继承TypeORM的Repository类并实现该接口,UserService通过依赖注入获取IUserRepository实例。项目编译正常,但调用控制器create方法时,UserRepository的addEntity方法里const newUser = this.create(user);抛出错误Cannot read properties of undefined (reading 'create'),试过网上方案没解决,求可行办法。
相关代码实现
仓库接口
import { CreateUserDto } from '../dto/create-user.dto'; import { UpdateUserDto } from '../dto/update-user.dto'; import { User } from '../entities/user.entity'; export interface IUserRepository { findAll(): Promise<User[]>; findById(id: number): Promise<User | undefined>; addEntity(user: CreateUserDto): Promise<User>; updateEntity( id: number, updateUserDto: UpdateUserDto, ): Promise<User | undefined>; deleteEntity(id: number): Promise<boolean>; } export const IUserRepository = Symbol('IUserRepository');
仓库实现类
import { Repository } from 'typeorm'; import { User } from './entities/user.entity'; import { CreateUserDto } from './dto/create-user.dto'; import { IUserRepository } from './interfaces/repository.interface'; import { UpdateUserDto } from './dto/update-user.dto'; export class UserRepository extends Repository<User> implements IUserRepository { async findAll(): Promise<User[]> { return this.find(); } async findById(id: number): Promise<User> { return this.findOneBy({ id: id }); } async addEntity(user: CreateUserDto): Promise<User> { const newUser = this.create(user); return this.save(newUser); } async updateEntity( id: number, updateUserDto: UpdateUserDto, ): Promise<User | undefined> { const user = await this.findById(id); if (!user) return undefined; Object.assign(user, updateUserDto); return this.save(user); } async deleteEntity(id: number): Promise<boolean> { const result = await this.delete(id); return result.affected > 0; } }
服务类
import { Inject, Injectable } from '@nestjs/common'; import { User } from './entities/user.entity'; import { CreateUserDto } from './dto/create-user.dto'; import { UpdateUserDto } from './dto/update-user.dto'; import { IUserRepository } from './interfaces/repository.interface'; @Injectable() export class UserService { constructor( @Inject(IUserRepository) private readonly userRepository: IUserRepository, ) {} async findAllUsers(): Promise<User[]> { return this.userRepository.findAll(); } async findUserById(id: number): Promise<User | undefined> { return this.userRepository.findById(id); } async createUser(createUserDto: CreateUserDto): Promise<User> { return this.userRepository.addEntity(createUserDto); } async updateUser( id: number, updateUserDto: UpdateUserDto, ): Promise<User | undefined> { return this.userRepository.updateEntity(id, updateUserDto); } async deleteUser(id: number): Promise<boolean> { return this.userRepository.deleteEntity(id); } }
模块类
import { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; import { User } from './entities/user.entity'; import { UserService } from './users.service'; import { UserController } from './users.controller'; import { UserRepository } from './users.repository'; import { IUserRepository } from './interfaces/repository.interface'; @Module({ imports: [TypeOrmModule.forFeature([User])], providers: [ UserService, { provide: IUserRepository, useClass: UserRepository }, ], controllers: [UserController], }) export class UsersModule {}
解决办法
这个错误的核心原因是TypeORM的Repository实例没有被正确初始化:直接用useClass注入UserRepository时,NestJS没有调用TypeORM的Repository工厂方法初始化内部核心属性(如manager、metadata),导致this指向的实例缺少TypeORM Repository的方法依赖。
方案1:标准自定义Repository注册(推荐)
修改UsersModule,把UserRepository通过TypeOrmModule.forFeature注册,让TypeORM自动完成实例初始化:
import { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; import { User } from './entities/user.entity'; import { UserService } from './users.service'; import { UserController } from './users.controller'; import { UserRepository } from './users.repository'; import { IUserRepository } from './interfaces/repository.interface'; @Module({ // 注册自定义Repository,让TypeORM负责初始化 imports: [TypeOrmModule.forFeature([User, UserRepository])], providers: [ UserService, { provide: IUserRepository, useClass: UserRepository }, ], controllers: [UserController], }) export class UsersModule {}
方案2:工厂函数手动注入依赖
如果不想通过TypeOrmModule注册自定义Repository,可通过工厂函数手动获取TypeORM初始化好的Repository实例并传递给自定义类:
import { Module } from '@nestjs/common'; import { TypeOrmModule, getRepositoryToken } from '@nestjs/typeorm'; import { User } from './entities/user.entity'; import { UserService } from './users.service'; import { UserController } from './users.controller'; import { UserRepository } from './users.repository'; import { IUserRepository } from './interfaces/repository.interface'; import { Repository } from 'typeorm'; @Module({ imports: [TypeOrmModule.forFeature([User])], providers: [ UserService, { provide: IUserRepository, useFactory: (userRepo: Repository<User>) => { const customRepo = new UserRepository(); // 复制TypeORM Repository的核心属性到自定义实例 Object.assign(customRepo, userRepo); return customRepo; }, inject: [getRepositoryToken(User)], }, ], controllers: [UserController], }) export class UsersModule {}
方案3:构造函数注入DataSource
修改UserRepository,通过构造函数接收TypeORM的DataSource,直接获取Repository实例:
// UserRepository.ts import { Repository, DataSource } from 'typeorm'; import { User } from './entities/user.entity'; import { CreateUserDto } from './dto/create-user.dto'; import { IUserRepository } from './interfaces/repository.interface'; import { UpdateUserDto } from './dto/update-user.dto'; export class UserRepository implements IUserRepository { private readonly repo: Repository<User>; constructor(dataSource: DataSource) { this.repo = dataSource.getRepository(User); } async findAll(): Promise<User[]> { return this.repo.find(); } async findById(id: number): Promise<User> { return this.repo.findOneBy({ id: id }); } async addEntity(user: CreateUserDto): Promise<User> { const newUser = this.repo.create(user); return this.repo.save(newUser); } async updateEntity( id: number, updateUserDto: UpdateUserDto, ): Promise<User | undefined> { const user = await this.repo.findOneBy({ id }); if (!user) return undefined; Object.assign(user, updateUserDto); return this.repo.save(user); } async deleteEntity(id: number): Promise<boolean> { const result = await this.repo.delete(id); return result.affected > 0; } }
然后在模块中配置注入:
// UsersModule.ts import { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; import { User } from './entities/user.entity'; import { UserService } from './users.service'; import { UserController } from './users.controller'; import { UserRepository } from './users.repository'; import { IUserRepository } from './interfaces/repository.interface'; import { DataSource } from 'typeorm'; @Module({ imports: [TypeOrmModule.forFeature([User])], providers: [ UserService, { provide: IUserRepository, useFactory: (dataSource: DataSource) => new UserRepository(dataSource), inject: [DataSource], }, ], controllers: [UserController], }) export class UsersModule {}
内容的提问来源于stack exchange,提问作者derstauner

