GraphQL Schema拼接后cmsMetaData返回null问题求助
排查GraphQL Schema拼接后cmsMetaData字段返回null的问题
已将两个远程GraphQL Schema拼接并部署在本地服务器,查询主Product类型下的cmsMetaData字段始终返回null。使用@graphql-tools/stitch完成Schema拼接,扩展Product类型添加cmsMetaData字段关联Strapi_Product,配置了解析器通过delegateToSchema从第二个Schema获取数据,但结果仍为null。以下是相关代码、查询语句及类型定义:
相关代码
主API路由文件
import { ApolloServer } from 'apollo-server-micro'; import { ApolloServerPluginInlineTraceDisabled, ApolloServerPluginLandingPageLocalDefault } from "apollo-server-core"; import { stitchSchemas } from '@graphql-tools/stitch'; import { delegateToSchema } from '@graphql-tools/delegate'; import { RenameTypes, RenameRootFields } from '@graphql-tools/wrap'; import createRemoteSchema from '../../utils/createRemoteExecutor'; // Configuration for Next.js API Routes export const config = { api: { bodyParser: false, }, }; // Export as a Next.js API Route export default async (req, res) => { // Setup subschema configurations const productsSubschema = await createRemoteSchema({ url: 'https://schema1.com/graphql/' }); const cmsSubschema = await createRemoteSchema({ url: 'https://schema2.com/graphql/', transforms: [ new RenameRootFields( (operationName, fieldName, fieldConfig) => `strapi_${fieldName}`, ), new RenameTypes((name) => `Strapi_${name}`), ], }); // Build the combined schema and set up the extended schema and resolver const schema = stitchSchemas({ subschemas: [productsSubschema, cmsSubschema], typeDefs: ` extend type Product { cmsMetaData: Strapi_Product } `, resolvers: { Product: { cmsMetaData: { selectionSet: `{ id }`, resolve(product, args, context, info) { // Get the data for the extended type from the subschema for Strapi return delegateToSchema({ schema: cmsSubschema, operation: 'query', fieldName: 'strapi_product', args: { where: { SaleorID: product.id } }, context, info, }); }, }, }, }, }); // Set up the GraphQL server const apolloServer = new ApolloServer({ schema, plugins: [ ApolloServerPluginInlineTraceDisabled(), ApolloServerPluginLandingPageLocalDefault({ embed: true }), ], }); await apolloServer.start(); const apolloServerHandler = apolloServer.createHandler({ path: '/api/graphql', }); // Return the GraphQL endpoint return apolloServerHandler(req, res); };
utils/createRemoteExecutor.js
import { introspectSchema, wrapSchema } from '@graphql-tools/wrap'; import { print } from 'graphql'; // Builds a remote schema executor function, // customize any way that you need (auth, headers, etc). // Expects to recieve an object with "document" and "variable" params, // and asynchronously returns a JSON response from the remote. export default async function createRemoteSchema({ url, ...filters }) { const executor = async ({ document, variables }) => { const query = print(document); const fetchResult = await fetch(url, { method: 'POST', headers: { // We can also do Authentication here 'Content-Type': 'application/json', }, body: JSON.stringify({ query, variables }), }); return fetchResult.json(); }; return wrapSchema({ schema: await introspectSchema(executor), executor, ...filters, }); }
查询语句
products(first: 100, channel: "default-channel") { edges { node { id name cmsMetaData { Title SaleorID } } } }
生成的类型定义
export type Product = Node & ObjectWithMetadata & { __typename?: 'Product'; ... cmsMetaData?: Maybe<Array<Maybe<Strapi_Product>>>; ... } export type Strapi_Product = { __typename?: 'Strapi_Product'; SaleorID?: Maybe<Scalars['String']>; Title?: Maybe<Scalars['String']>; createdAt?: Maybe<Scalars['Strapi_DateTime']>; publishedAt?: Maybe<Scalars['Strapi_DateTime']>; updatedAt?: Maybe<Scalars['Strapi_DateTime']>; };
查询结果
{ "data": { "products": { "edges": [ { "node": { "id": "UHJvZHVjdDoxMjc=", "name": "52-00 Base Plate", "cmsMetaData": null } }, { "node": { "id": "UHJvZHVjdDoxMjg=", "name": "52-01HD Weigh Module", "cmsMetaData": null } } ] } } }
排查方向
- 字段类型不匹配:类型定义中
cmsMetaData是数组类型Maybe<Array<Maybe<Strapi_Product>>>,但扩展类型时定义为单个Strapi_Product,类型不匹配会导致数据无法映射返回null。需统一类型:要么修改扩展类型为[Strapi_Product],要么调整解析器处理数组结果。 - 关联字段格式不一致:主Schema的
product.id是Base64编码值(如UHJvZHVjdDoxMjc=解码后为Product:127),需确认Strapi侧SaleorID存储的是原始ID还是编码后的值,若格式不同需在解析器中解码product.id后再传入参数。 - 远程查询字段错误:手动请求Strapi的GraphQL端点,验证
strapi_product字段是否存在,以及where: { SaleorID: xxx }是否能返回有效数据。 - 解析器参数传递问题:在解析器中添加日志打印
product.id和传入的args,确认参数是否正确传递到远程查询;同时检查delegateToSchema的schema是否指向正确的cmsSubschema。 - 远程权限缺失:若Strapi需要认证才能查询,需在
createRemoteSchema的fetch请求headers中添加认证字段(如Authorization: Bearer xxx)。 - 错误捕获缺失:在解析器中添加try-catch块,捕获
delegateToSchema可能抛出的错误,排查是否存在网络错误、字段不存在等问题。
内容的提问来源于stack exchange,提问作者Laurence Cope
相关产品推荐
相关产品推荐

