You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何以DRY方式将Kysely与NestJS管道验证集成

维持DDL唯一数据源的NestJS DTO验证方案

针对你用Kysely+NestJS,以DDL为唯一数据源但无法直接用kysely-codegen生成带class-validator注解类的问题,以下几个DRY方案可以解决:

方案1:自定义kysely-codegen模板生成带验证注解的类

kysely-codegen支持通过--template参数指定自定义EJS模板,你可以修改默认模板,让它直接生成带有class-validator注解的类(而非接口),模板会根据DDL里的字段约束(非空、数据类型、长度等)自动映射对应的验证装饰器。

步骤:

  1. 复制kysely-codegen的默认模板(可从其源码中找到default-template.ejs)
  2. 修改模板结构,将接口定义改为类定义,并添加验证注解逻辑
  3. 运行kysely-codegen时指定自定义模板:
    kysely-codegen --template ./path/to/your-custom-template.ejs
    

示例模板片段:

import { IsString, IsInt, IsNotEmpty, IsOptional } from 'class-validator';

export class <%= entity.name %>Dto {
  <% entity.columns.forEach(column => { %>
    <% if (column.notNull) { %>@IsNotEmpty()<% } else { %>@IsOptional()<% } %>
    <% if (column.dataType === 'varchar') { %>@IsString()<% } %>
    <% if (column.dataType === 'int') { %>@IsInt()<% } %>
    <%= column.name %>: <%= column.tsType %>;
  <% }) %>
}

每次更新DDL后重新生成,类会自动带上对应验证注解,完全基于DDL约束,无需手动同步。

方案2:用ts-morph二次生成验证类

如果不想修改kysely-codegen的模板,可写一个Node.js脚本,用ts-morph解析kysely-codegen生成的接口文件,自动生成带class-validator注解的DTO类。

步骤:

  1. 安装依赖:
    npm install ts-morph --save-dev
    
  2. 编写生成脚本(比如generate-dtos.ts):
    import { Project, InterfaceDeclaration } from 'ts-morph';
    
    const project = new Project();
    // 读取kysely生成的接口文件
    const sourceFile = project.addSourceFileAtPath('./src/db/types.ts');
    
    // 遍历所有接口生成对应DTO
    sourceFile.getInterfaces().forEach(iface => {
      const dtoFile = project.createSourceFile(`./src/dtos/${iface.getName()}Dto.ts`, '', { overwrite: true });
      
      // 导入class-validator装饰器
      dtoFile.addImportDeclaration({
        namedImports: ['IsString', 'IsInt', 'IsNotEmpty', 'IsOptional'],
        moduleSpecifier: 'class-validator',
      });
    
      // 创建DTO类
      const dtoClass = dtoFile.addClass({ name: `${iface.getName()}Dto` });
    
      // 给类属性添加验证注解
      iface.getProperties().forEach(prop => {
        const decorators = [];
        // 处理非空/可选约束
        decorators.push(prop.isOptional() ? '@IsOptional()' : '@IsNotEmpty()');
        // 处理数据类型
        const propType = prop.getType().getText();
        if (propType === 'string') decorators.push('@IsString()');
        if (propType === 'number') decorators.push('@IsInt()');
    
        dtoClass.addProperty({
          name: prop.getName(),
          type: propType,
          decorators: decorators.map(d => ({ name: d })),
        });
      });
    });
    
    project.save();
    
  3. 将脚本加入npm脚本,和kysely-codegen联动:
    "scripts": {
      "db:generate": "kysely-codegen && ts-node ./generate-dtos.ts"
    }
    

每次更新DDL后运行npm run db:generate,就能自动生成接口和对应的验证DTO类,全程无需手动维护。

方案3:基于Kysely元数据的动态验证(运行时)

如果不想生成任何类,可利用Kysely的数据库元数据,在NestJS自定义管道中实现动态验证,直接基于DDL约束校验请求体。

示例自定义管道:

import { PipeTransform, Injectable, BadRequestException } from '@nestjs/common';
import { db } from './db/connection'; // 你的Kysely实例

@Injectable()
export class DbSchemaValidationPipe implements PipeTransform {
  constructor(private readonly tableName: string) {}

  async transform(value: any) {
    const tableSchema = await db.introspection.getTable(this.tableName);
    if (!tableSchema) throw new BadRequestException(`表 ${this.tableName} 不存在`);

    const errors: string[] = [];
    for (const column of tableSchema.columns) {
      const fieldValue = value[column.name];
      
      // 校验非空约束
      if (column.notNull && (fieldValue === undefined || fieldValue === null)) {
        errors.push(`${column.name} 为必填项`);
      }
      
      // 校验数据类型
      if (fieldValue !== undefined) {
        if (column.dataType.includes('varchar') && typeof fieldValue !== 'string') {
          errors.push(`${column.name} 必须为字符串类型`);
        } else if (column.dataType.includes('int') && !Number.isInteger(fieldValue)) {
          errors.push(`${column.name} 必须为整数`);
        }
      }
    }

    if (errors.length) throw new BadRequestException(errors);
    return value;
  }
}

控制器中使用:

@Post()
async create(@Body(new DbSchemaValidationPipe('users')) user: Users) {
  // 业务逻辑处理
}

这个方案完全依赖DDL元数据,无需生成DTO,但验证逻辑需要自行扩展,且属于运行时验证,性能略低于编译时生成的类。


内容的提问来源于stack exchange,提问作者Marcus Junius Brutus

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 18:45:57