NestJS结合Prisma如何编写匹配一对多关联类型的DTO
问题1:是否可以编写和Prisma自动生成类型完全匹配的DTO?
可以,但通常不建议直接将对外暴露的API DTO和Prisma内部类型完全对齐:
- 手动编写的场景下,你需要完全复刻Prisma生成的嵌套输入类型结构,包括
create/connect/connectOrCreate等操作字段,同时要适配Prisma的Enumerable(支持单个值或数组)、XOR等工具类型的约束,代码冗余度高、维护成本大。 - 也可以使用
prisma-nestjs-dto-generator这类开源工具,直接根据你的Prisma Schema生成匹配的DTO类,不需要手动编写。 - 对外API DTO如果完全对齐Prisma内部类型,会把数据库操作的细节暴露给前端,增加安全风险,也会让前端接口调用逻辑和Prisma的实现强绑定,不利于后续迭代。
问题2:如何调整orderable_bundle的DTO,为嵌套的orderable_test字段添加create包装层?
有两种常见的实现方案,你可以根据业务需求选择:
方案1:DTO直接对齐Prisma创建格式
直接修改DTO结构匹配Prisma要求的嵌套创建格式,参数可以直接传给Prisma的create方法无需额外转换:
// 去掉orderable_bundleId的测试创建DTO,创建关联时该字段会由Prisma自动维护 class OrderableTestCreateDTO { @ApiProperty() test_name: string; @ApiProperty() test_id: number; @ApiProperty() price: number; @ApiProperty() is_orderable: boolean; @ApiProperty() is_active: boolean; } // 嵌套测试创建的输入DTO,匹配Prisma的嵌套操作结构 class OrderableTestNestedInputDTO { @ApiPropertyOptional({ type: [OrderableTestCreateDTO] }) create?: OrderableTestCreateDTO[]; // 如果需要支持关联已有的测试,还可以补充connect等字段 // @ApiPropertyOptional({ type: [Number] }) // connect?: {id: number}[]; } // 调整后的套餐DTO class OrderableBundleDTO { @ApiProperty() bundle_name: string; @ApiProperty() bundle_id: number; @ApiProperty() price: number; @ApiProperty() is_orderable: boolean; @ApiProperty() is_active: boolean; @ApiPropertyOptional({ type: () => OrderableTestNestedInputDTO }) orderable_tests?: OrderableTestNestedInputDTO; }
方案2:对外DTO保持简洁,Service层做格式转换
如果不希望前端感知Prisma的内部格式,前端传参直接传测试数组即可,你可以在Service层手动包装create层:
首先对外的套餐DTO调整为数组类型(一个套餐对应多个测试):
class OrderableBundleDTO { @ApiProperty() bundle_name: string; @ApiProperty() bundle_id: number; @ApiProperty() price: number; @ApiProperty() is_orderable: boolean; @ApiProperty() is_active: boolean; @ApiPropertyOptional({ type: [OrderableTestDTO] }) orderable_tests?: OrderableTestDTO[]; }
然后在Service的创建逻辑里手动添加create层:
async createBundle(dto: OrderableBundleDTO) { const { orderable_tests, ...bundleData } = dto; return this.prisma.orderable_bundle.create({ data: { ...bundleData, // 存在测试数据时自动包装create层 ...(orderable_tests?.length && { orderable_tests: { create: orderable_tests.map(test => { // 过滤掉不需要的关联ID字段 const { orderable_bundleId, ...testData } = test; return testData; }) } }) } }) }
这种方案对外接口更友好,和Prisma实现解耦,更推荐业务系统使用。
内容的提问来源于stack exchange,提问作者Zichzheng
相关产品推荐
相关产品推荐

