在NestJS/Prisma/GraphQL中Decimal转number失败的问题
问题描述
我有一个类型为Decimal的字段,从客户端获取数据时返回的是string类型,但预期应为number类型。
对象类型定义:
import { Decimal } from '@prisma/client/runtime/library'; import { GraphQLDecimal } from 'prisma-graphql-type-decimal'; @ObjectType() export class Supplier { @Field(() => GraphQLDecimal, {nullable:false}) commission!: Decimal; }
返回结果:
{ // 其他字段.... id: 7; isConfirmed: false; webAppLanguage: 'fr'; // 应为number类型 commission: '0.3'; }
我尝试通过NestJS字段级中间件将Decimal转为number,但未生效:
export const decimalCastMiddleWare: FieldMiddleware = async ( ctx: MiddlewareContext, next: NextFn, ) => { let value = await next(); if (Decimal.isDecimal(value)) { value = value.toNumber(); } return value; }; // 字段级中间件配置 { buildSchemaOptions: { // 其他ApolloDriverConfig配置项... fieldMiddleware: [decimalCastMiddleWare] } }
问题原因
你使用的GraphQLDecimal标量默认会把Decimal对象序列化为string返回,所以字段中间件拿到的value已经是string类型,导致Decimal.isDecimal(value)判断不成立,转换逻辑无法执行。
解决方案
方案一:自定义标量类型替换GraphQLDecimal
自己实现一个标量,在序列化阶段直接把Decimal转成number返回:
import { Scalar, CustomScalar } from '@nestjs/graphql'; import { Decimal } from '@prisma/client/runtime/library'; import { ValueNode, Kind } from 'graphql'; @Scalar('DecimalNumber', () => Number) export class DecimalNumberScalar implements CustomScalar<Decimal, number> { description = 'Decimal转number的自定义标量'; serialize(value: Decimal): number { return value.toNumber(); } parseValue(value: string | number): Decimal { return new Decimal(value); } parseLiteral(ast: ValueNode): Decimal { if (ast.kind === Kind.FLOAT || ast.kind === Kind.INT) { return new Decimal(ast.value); } throw new Error('该标量仅支持解析浮点数/整数字面量'); } }
在模块中注册这个标量,然后修改Supplier类的字段定义:
@ObjectType() export class Supplier { @Field(() => DecimalNumberScalar, {nullable:false}) commission!: Decimal; }
方案二:调整中间件逻辑兼容string转number
如果不想自定义标量,可以修改中间件,增加对string类型的判断转换:
export const decimalCastMiddleWare: FieldMiddleware = async ( ctx: MiddlewareContext, next: NextFn, ) => { let value = await next(); if (Decimal.isDecimal(value)) { value = value.toNumber(); } else if (typeof value === 'string' && !isNaN(parseFloat(value))) { value = parseFloat(value); } return value; };
方案三:Prisma查询阶段直接转换
在查询数据时,直接把Decimal字段转为number:
// 示例查询逻辑 const supplier = await prisma.supplier.findUnique({ where: { id: 7 }, select: { id: true, isConfirmed: true, webAppLanguage: true, commission: true, }, }); if (supplier) { supplier.commission = supplier.commission.toNumber(); }
注意事项
- 使用
toNumber()转换Decimal可能丢失精度(比如极大/极小的小数),如果业务对精度要求高,建议保留string返回,由客户端自行处理。 - 自定义标量时,要确保客户端传入的参数能正确转换为Decimal,避免数据异常。
内容的提问来源于stack exchange,提问作者Meds
相关产品推荐
相关产品推荐

