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

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

问题原因

  1. GraphQL不支持直接在字段类型中用|表示多类型,必须通过联合类型(Union)定义。
  2. 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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 14:40:18