GraphQL Schema定义联合返回类型的报错问题求助
问题:GraphQL联合类型无法包含标量类型的解决方法
问题背景
使用Node.js的Apollo Server开发时,需要新增affiliateBalance查询,返回值有两种情况:要么是保留两位小数的Float值(如47.28),要么是AffiliateBalance对象(结构为{ pendingBalance: 两位小数数值, qualifiedBalance: 两位小数数值 })。
尝试的方案及报错
方案1:直接使用Float! | AffiliateBalance!作为返回类型
代码:
import { gql } from 'apollo-server-core'; export const querySchema = gql` type AffiliateBalance { pendingBalance: Float! qualifiedBalance: Float! } type Query { me: User! users(input: PaginationInput): [User!]! user(id: Int!): User! referrals(input: GetReferralsInput): GetReferralsResponse! affiliateTransactions( limit: Int skip: Int type: TransactionType ): GetAffiliateTransactionsResponse! affiliatePerformanceMetrics: AffiliatePerformanceMetricsResponse! affiliateSessions(limit: Int, skip: Int): GetAffiliateSessionsResponse! affiliateSessionMetrics: AffiliateSessionMetricsResponse! affiliateBalancePending: Float! affiliateBalanceQualified: Float! affiliateBalance: Float! | AffiliateBalance! } `;
报错信息:
/Users/nick/Documents/Coding/Projects/Practice/PERNTG/server/node_modules/graphql/error/syntaxError.js:15 return new _GraphQLError.GraphQLError(`Syntax Error: ${description}`, { ^ GraphQLError: Syntax Error: Expected Name, found "|". at syntaxError (/Users/nick/Documents/Coding/Projects/Practice/PERNTG/server/node_modules/graphql/error/syntaxError.js:15:10) at Parser.expectToken (/Users/nick/Documents/Coding/Projects/Practice/PERNTG/server/node_modules/graphql/language/parser.js:1413:40) at Parser.parseName (/Users/nick/Documents/Coding/Projects/Practice/PERNTG/server/node_modules/graphql/language/parser.js:107:24) at Parser.parseFieldDefinition (/Users/nick/Documents/Coding/Projects/Practice/PERNTG/server/node_modules/graphql/language/parser.js:845:23) at Parser.optionalMany (/Users/nick/Documents/Coding/Projects/Practice/PERNTG/server/node_modules/graphql/language/parser.js:1510:28) at Parser.parseFieldsDefinition (/Users/nick/Documents/Coding/Projects/Practice/PERNTG/server/node_modules/graphql/language/parser.js:831:17) at Parser.parseObjectTypeDefinition (/Users/nick/Documents/Coding/Projects/Practice/PERNTG/server/node_modules/graphql/language/parser.js:803:25) at Parser.parseDefinition (/Users/nick/Documents/Coding/Projects/Practice/PERNTG/server/node_modules/graphql/language/parser.js:171:23) at Parser.many (/Users/nick/Documents/Coding/Projects/Practice/PERNTG/server/node_modules/graphql/language/parser.js:1529:26) at Parser.parseDocument (/Users/nick/Documents/Coding/Projects/Practice/PERNTG/server/node_modules/graphql/language/parser.js:121:25) { path: undefined, locations: [ { line: 22, column: 30 } ], extensions: [Object: null prototype] {}}
方案2:定义联合类型包含Float和AffiliateBalance
代码:
import { gql } from 'apollo-server-core'; export const querySchema = gql` type AffiliateBalance { pendingBalance: Float! qualifiedBalance: Float! } union RawAffiliateBalance = Float | AffiliateBalance type Query { me: User! users(input: PaginationInput): [User!]! user(id: Int!): User! referrals(input: GetReferralsInput): GetReferralsResponse! affiliateTransactions( limit: Int skip: Int type: TransactionType ): GetAffiliateTransactionsResponse! affiliatePerformanceMetrics: AffiliatePerformanceMetricsResponse! affiliateSessions(limit: Int, skip: Int): GetAffiliateSessionsResponse! affiliateSessionMetrics: AffiliateSessionMetricsResponse! affiliateBalancePending: Float! affiliateBalanceQualified: Float! affiliateBalance: RawAffiliateBalance! } `;
报错信息:
/Users/nick/Documents/Coding/Projects/Practice/PERNTG/server/node_modules/graphql/type/validate.js:59 throw new Error(errors.map((error) => error.message).join('\n\n')); ^ Error: Union type RawAffiliateBalance can only include Object types, it cannot include Float. at assertValidSchema (/Users/nick/Documents/Coding/Projects/Practice/PERNTG/server/node_modules/graphql/type/validate.js:59:11) at assertValidExecutionArguments (/Users/nick/Documents/Coding/Projects/Practice/PERNTG/server/node_modules/graphql/execution/execute.js:194:35) at execute (/Users/nick/Documents/Coding/Projects/Practice/PERNTG/server/node_modules/graphql/execution/execute.js:113:3) at generateSchemaHash (/Users/nick/Documents/Coding/Projects/Practice/PERNTG/server/node_modules/apollo-server-core/src/utils/schemaHash.ts:25:25) at ApolloServer.generateSchemaDerivedData (/Users/nick/Documents/Coding/Projects/Practice/PERNTG/server/node_modules/apollo-server-core/src/ApolloServer.ts:716:42) at Object.schemaDerivedDataProvider (/Users/nick/Documents/Coding/Projects/Practice/PERNTG/server/node_modules/apollo-server-core/src/ApolloServer.ts:333:18) at new SchemaManager (/Users/nick/Documents/Coding/Projects/Practice/PERNTG/server/node_modules/apollo-server-core/src/utils/schemaManager.ts:76:36) at new ApolloServerBase (/Users/nick/Documents/Coding/Projects/Practice/PERNTG/server/node_modules/apollo-server-core/src/ApolloServer.ts:328:24) at new ApolloServer (/Users/nick/Documents/Coding/Projects/Practice/PERNTG/server/node_modules/apollo-server-express/src/ApolloServer.ts:55:1) at /Users/nick/Documents/Coding/Projects/Practice/PERNTG/server/src/index.ts:20:48
问题原因
- GraphQL不支持直接在字段类型中用
|表示多类型,必须通过联合类型(Union)定义。 - GraphQL的联合类型仅允许包含对象类型(Object Type),不能包含Float、Int这类标量类型,这是语法规则限制。
解决方法
方案1:将标量包装为对象类型
定义一个包装Float的对象,让联合类型包含该对象与原AffiliateBalance:
import { gql } from 'apollo-server-core'; export const querySchema = gql` type AffiliateBalance { pendingBalance: Float! qualifiedBalance: Float! } # 用对象包装Float标量 type ScalarBalance { value: Float! } union RawAffiliateBalance = ScalarBalance | AffiliateBalance type Query { me: User! users(input: PaginationInput): [User!]! user(id: Int!): User! referrals(input: GetReferralsInput): GetReferralsResponse! affiliateTransactions( limit: Int skip: Int type: TransactionType ): GetAffiliateTransactionsResponse! affiliatePerformanceMetrics: AffiliatePerformanceMetricsResponse! affiliateSessions(limit: Int, skip: Int): GetAffiliateSessionsResponse! affiliateSessionMetrics: AffiliateSessionMetricsResponse! affiliateBalancePending: Float! affiliateBalanceQualified: Float! affiliateBalance: RawAffiliateBalance! } `;
- Resolver处理:返回单个数值时返回
{ value: 47.28 },返回分类型余额时返回原AffiliateBalance结构。 - 客户端查询需用片段区分类型:
query { affiliateBalance { ... on ScalarBalance { value } ... on AffiliateBalance { pendingBalance qualifiedBalance } } }
方案2:统一返回对象类型,兼容两种场景
调整查询返回值,始终返回AffiliateBalance对象,通过字段标记或重复数值兼容单一余额场景:
import { gql } from 'apollo-server-core'; export const querySchema = gql` type AffiliateBalance { pendingBalance: Float! qualifiedBalance: Float! # 可选:标记是否为单一余额场景 isSingleValue: Boolean! } type Query { me: User! users(input: PaginationInput): [User!]! user(id: Int!): User! referrals(input: GetReferralsInput): GetReferralsResponse! affiliateTransactions( limit: Int skip: Int type: TransactionType ): GetAffiliateTransactionsResponse! affiliatePerformanceMetrics: AffiliatePerformanceMetricsResponse! affiliateSessions(limit: Int, skip: Int): GetAffiliateSessionsResponse! affiliateSessionMetrics: AffiliateSessionMetricsResponse! affiliateBalancePending: Float! affiliateBalanceQualified: Float! affiliateBalance: AffiliateBalance! } `;
- Resolver处理:
- 返回单一数值时:
affiliateBalance: () => ({ pendingBalance: 47.28, qualifiedBalance: 47.28, isSingleValue: true })- 返回分类型余额时:
affiliateBalance: () => ({ pendingBalance: 20.5, qualifiedBalance: 26.78, isSingleValue: false })
额外提示
GraphQL的Float类型本身不支持精度限制,需在Resolver中处理数值精度,比如用Math.round(value * 100) / 100或Number(value.toFixed(2))确保返回两位小数的数值。
内容的提问来源于stack exchange,提问作者user8758206
相关产品推荐
相关产品推荐

