NestJS是否内置类似Loopback/Sails.js的CRUD生成工具?如何复用控制器代码?
NestJS的CRUD实现方案:告别重复代码
嘿,很高兴你认可NestJS的设计理念!确实,在每个Controller里重复写CRUD逻辑太折磨人了——NestJS本身虽然没有像LoopBack或Sails.js那样完全内置的一键式CRUD生成工具,但它凭借灵活的生态和可扩展的架构,完全能帮你快速实现无重复的CRUD功能,甚至比内置工具更适配自定义需求。
下面分两种常用方案来具体说明:
方案一:自定义通用Base层(高度可控)
如果需要完全掌控CRUD的逻辑细节,你可以自己封装通用的BaseService和BaseController,让所有业务模块继承它们,从根源上消除重复代码。
1. 编写通用BaseService
import { Repository } from 'typeorm'; // 泛型类适配任意实体类型 export abstract class BaseService<T> { constructor(protected readonly repo: Repository<T>) {} // 获取所有数据 async findAll(): Promise<T[]> { return this.repo.find(); } // 根据ID单条查询 async findOne(id: number): Promise<T | null> { return this.repo.findOneBy({ id } as any); } // 创建数据 async create(data: Partial<T>): Promise<T> { const entity = this.repo.create(data); return this.repo.save(entity); } // 更新数据 async update(id: number, data: Partial<T>): Promise<T | null> { await this.repo.update(id, data); return this.repo.findOneBy({ id } as any); } // 删除数据 async delete(id: number): Promise<void> { await this.repo.delete(id); } }
2. 业务模块继承Base层
比如你有一个User模块:
- 先定义User实体:
import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm'; @Entity() export class User { @PrimaryGeneratedColumn() id: number; @Column() name: string; @Column({ unique: true }) email: string; }
- 编写UserService继承BaseService:
import { Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { Repository } from 'typeorm'; import { BaseService } from '../common/base.service'; import { User } from './user.entity'; @Injectable() export class UsersService extends BaseService<User> { constructor(@InjectRepository(User) repo: Repository<User>) { super(repo); } // 这里可以添加User专属的业务逻辑,比如根据邮箱查询 async findByEmail(email: string): Promise<User | null> { return this.repo.findOneBy({ email }); } }
- 编写UserController调用通用方法:
import { Controller, Get, Post, Put, Delete, Param, Body } from '@nestjs/common'; import { UsersService } from './users.service'; import { User } from './user.entity'; @Controller('users') export class UsersController { constructor(private readonly usersService: UsersService) {} @Get() findAll(): Promise<User[]> { return this.usersService.findAll(); } @Get(':id') findOne(@Param('id') id: string): Promise<User | null> { return this.usersService.findOne(+id); } @Post() create(@Body() data: Partial<User>): Promise<User> { return this.usersService.create(data); } @Put(':id') update(@Param('id') id: string, @Body() data: Partial<User>): Promise<User | null> { return this.usersService.update(+id, data); } @Delete(':id') delete(@Param('id') id: string): Promise<void> { return this.usersService.delete(+id); } }
方案二:使用社区成熟CRUD库(快速开箱)
如果你想要类似LoopBack的一键生成效果,社区维护的@nestjsx/crud库是绝佳选择——它能自动生成标准CRUD路由,还支持分页、过滤、排序、字段选择等常用功能,几乎不用写额外代码。
1. 安装依赖
npm install @nestjsx/crud @nestjsx/crud-typeorm @nestjs/typeorm typeorm
2. 快速生成CRUD
还是以User模块为例:
- 定义User实体(和上面一致)
- 编写UsersService继承CrudService:
import { Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { Repository } from 'typeorm'; import { CrudService } from '@nestjsx/crud-typeorm'; import { User } from './user.entity'; @Injectable() export class UsersService extends CrudService<User> { constructor(@InjectRepository(User) repo: Repository<User>) { super(repo); } }
- 编写UsersController继承CrudController:
import { Controller } from '@nestjs/common'; import { Crud, CrudController } from '@nestjsx/crud'; import { User } from './user.entity'; import { UsersService } from './users.service'; @Crud({ model: { type: User, }, // 可以在这里配置路由规则、字段权限、分页参数等 routes: { only: ['getManyBase', 'getOneBase', 'createOneBase', 'updateOneBase', 'deleteOneBase'], }, }) @Controller('users') export class UsersController implements CrudController<User> { constructor(public service: UsersService) {} }
这样配置后,NestJS会自动生成以下路由:
GET /users:获取列表(支持分页?page=1&limit=10、过滤?filter[name]=John、排序?sort=id,DESC)GET /users/:id:获取单条数据POST /users:创建数据PUT /users/:id:更新数据DELETE /users/:id:删除数据
总结
NestJS虽然没有内置的CRUD生成器,但通过自定义通用层或者社区CRUD库,完全能实现和LoopBack/Sails.js一样的高效CRUD开发,而且灵活性更高——你可以根据业务需求选择完全定制,或者快速开箱。
内容的提问来源于stack exchange,提问作者Yaroslav Draha
相关产品推荐
相关产品推荐

