Skip to content

@lzui/nest-crud

一个基于 NestJS 的高效 CRUD 库,让您专注于业务逻辑而非重复的 API 实现。

🌟 特性

  • 🚀 快速生成 API - 只需几行代码即可创建完整的 CRUD 接口
  • 🎨 灵活配置 - 支持路由前缀、API 选择、参数处理等多种自定义选项
  • 📦 分页查询 - 内置强大的分页和筛选功能,支持复杂查询条件
  • 🔧 易于扩展 - 支持自定义控制器、服务方法和权限验证
  • 🛡️ 安全可靠 - 内置权限控制机制,支持细粒度的 API 权限管理
  • 📚 完善文档 - 提供详细的 API 参考和示例项目

🚀 快速开始

安装

bash
pnpm add @lzui/nest-crud

基本使用

1. 创建控制器

typescript
import { Get, Inject, UseGuards } from '@nestjs/common';
import { CrudController, BaseController, Auth, AuthGuard } from '@lzui/nest-crud';
import { DemoEntity } from './entities/demo.entity';
import { DemoService } from './demo.service';
@CrudController({
  prefix: 'api/v1/demo',
  /* 添加通用CRUD接口 */
  api: ['add', 'delete', 'update', 'info', 'list', 'page', 'export'],
  /* 设置表实体  */
  entity: DemoEntity,
  /* 权限验证配置 */
  authOptions: {
    /* 要求接口需要权限验证 */
    requireAuthFor: [
      'demo:add',
      'demo:delete',
      'demo:update',
      'demo:info',
      'demo:export',
      'demo:list',
      'demo:page',
    ],
    /* 排除list和page接口的权限验证, 优先级高于requireAuthFor */
    excludeAuthFor: [
      'demo:add',
      'demo:delete',
      'demo:update',
      'demo:info',
      'demo:export',
      'demo:list',
      'demo:page',
    ],
    /** 实体名称,用于权限验证 */
    entityName: 'demo',
  },
  /* add接口 */
  insertParam: (ctx: any) => {
    /** 从请求上下文获取当前用户ID */
    const userId = ctx.user?.id;
    /** 从请求体中获取其他字段,过滤等其他业务操作 */
    return {
      ...ctx.body,
      userId: userId || 'insertParam',
      createBy: userId || 'insertParam',
      createTime: new Date(),
      updateTime: new Date(),
    };
  },
  /** update 接口 */
  updateParam: (ctx: any) => {
    /** 从请求上下文获取当前用户ID */
    const userId = ctx.user?.id;
    return {
      ...ctx.body,
      updateBy: userId || 'updateParam',
      updateTime: new Date(),
    };
  },
  /** export 接口 */
  exportParam: (ctx: any) => {
    /** 配置导出接口参数 */
    const defaultExportConfig = {
      header: [
        { title: 'ID', dataIndex: 'id' },
        { title: '状态', dataIndex: 'status' },
        { title: '创建人', dataIndex: 'create_by' },
        { title: '价格', dataIndex: 'price' },
        { title: '标题', dataIndex: 'title' },
        { title: '类型', dataIndex: 'type' },
      ],
      sheetName: '演示数据',
      fileName: 'demo数据',
    };

    const exportConfig = ctx.body?.exportConfig || defaultExportConfig;
    return {
      ...ctx.body,
      exportConfig,
    };
  },
  /** info查询时忽略的字段 */
  infoIgnoreProperty: ['userId', 'data'],
  /* 分页查询配置 */
  pageQueryOp: {
    /* 优先级1 请求参数为keyWord, 支持模糊查询 多个key*/
    keyWordLikeFields: ['title', 'type'],
    /* 优先级2 等于查询 */
    fieldEq: ['type'],
    /** 优先级3 支持模糊查询的字段 */
    fieldLike: ['title', 'userId', 'type'],
    /* 指定返回字段,使用实体属性名 使用点符号指定关联表的字段 */
    select: [
      'id',
      'title',
      'price',
      'type',
      'userId',
      'status',
      'createTime',
      'delFlag',
      // app 关联字段
      // 'user.userId',
      'user.ext',
      'user.fileName',
      'user.newFileName',
      'user.url',
      // app 关联字段
      // 'app.appId',
      'app.fileName',
      'app.demoId',
      'app.ext',
    ],
    /* 启用关系查询,加载关联的user数据 */
    relations: ['user', 'app'],
    /* 自定义关联字段的键名映射 */
    relationAlias: {
      user: 'customUser', // 将返回数据中的"user"键名改为"customUser"
      app: 'customApp', // 将返回数据中的"apps"键名改为"customApp"
    },
    /* 设置关系类型 */
    relationType: {
      user: 'one', // 一对一关系
      app: 'many', // 一对多关系
    },
    /* 增加其他条件 */
    where: async (params: any) => {
      console.log(params);
      /* 自定义查询条件 */
      return [
        {
          // status: '1',
          // delFlag: '1',
        },
      ];
    },
    /** 分页查询排序 */
    addOrderBy: {
      createTime: 'DESC',
    },
  },
})
export class DemoController extends BaseController {
  constructor(
    @Inject(DemoService)
    protected readonly service: DemoService,
  ) {
    super();
  }

  /**
   * 其他接口
   */
  @UseGuards(AuthGuard)
  @Auth('demo:other')
  @Get('/other')
  async other() {
    const res = await this.service.list();
    return this.ok(res);
  }
}

2. 创建服务

typescript
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { BaseService } from '@lzui/nest-crud';
import { DemoEntity } from './entities/demo.entity';

@Injectable()
export class DemoService extends BaseService<DemoEntity> {
  constructor(
    @InjectRepository(DemoEntity)
    repository: Repository<DemoEntity>,
  ) {
    super(repository);
  }
}

3. 创建module

typescript
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { DemoController } from './demo.controller';
import { DemoService } from './demo.service';
import { DemoEntity } from './entities/demo.entity';

@Module({
  imports: [TypeOrmModule.forFeature([DemoEntity])],
  controllers: [DemoController],
  providers: [DemoService],
  exports: [DemoService],
})
export class DemoModule {}

4. 创建实体

typescript
import { Entity, Column, ManyToOne, OneToMany, JoinColumn } from 'typeorm';
import { BaseStatusByIdTimeDelFlagEntity } from '@lzui/nest-crud';
import { AppEntity } from '../../app/entities/app.entity';
import { UserEntity } from '../../user/entities/user.entity';


@Entity('demo', { comment: '演示' })
export class DemoEntity extends BaseStatusByIdTimeDelFlagEntity {
  @Column({ type: 'varchar', length: 255 })
  title: string;

  @Column({ type: 'decimal', precision: 10, scale: 2 })
  price: number;

  @Column({ type: 'varchar', length: 50, nullable: true })
  type: string;

  @Column({ type: 'text', nullable: true })
  data: string;

  @Column({ type: 'varchar', length: 36, nullable: true, name: 'user_id' })
  userId: string;

  // 添加与UserEntity的关系
  @ManyToOne(() => UserEntity, { createForeignKeyConstraints: false })
  @JoinColumn({ name: 'user_id' })
  user: UserEntity;

  // 一对多关系:一个DemoEntity对应多个AppEntity
  @OneToMany(() => AppEntity, (app) => app.demo)
  app: AppEntity[];
}

📚 文档目录