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

如何用graphql-tools将远程GraphQL Schema的Viewer字段移至Query类型?

解决远程GraphQL Schema中Viewer字段迁移到Query类型的方案

我之前刚好处理过类似的Schema转换需求,其实核心思路是先获取Viewer实例,再以它为父对象委托调用对应的字段,下面给你一步步拆解可行的实现方式:

核心思路

远程Schema的Viewer字段依赖Viewer类型作为父节点,而delegateToSchema默认是从根类型(Query/Mutation)开始委托,所以我们需要分两步走:

  1. 先通过远程Schema的根查询获取Viewer实例
  2. 将这个Viewer实例作为父对象,委托调用它身上的目标字段

具体实现步骤

1. 先获取并封装远程Schema

首先我们需要先拿到远程Schema的可执行实例,这里用@graphql-tools/wrap的工具来处理:

const { introspectSchema, makeRemoteExecutableSchema } = require('@graphql-tools/wrap');
const { HttpLink } = require('@apollo/client');
const fetch = require('cross-fetch');

async function getRemoteSchema() {
  // 替换成你的远程GraphQL API地址
  const link = new HttpLink({ uri: 'https://your-remote-api.com/graphql', fetch });
  const remoteSchema = await introspectSchema(link);
  return makeRemoteExecutableSchema({
    schema: remoteSchema,
    link,
  });
}

2. 定义本地Schema扩展

我们需要在本地Query类型里新增要迁移的字段,和远程Viewer上的字段保持一致的参数和返回类型:

# 本地扩展Schema,把Viewer的字段加到Query上
extend type Query {
  user(id: ID!): User
  latestPosts(limit: Int = 10): [Post!]!
  # 其他你需要迁移的Viewer字段
}

3. 编写自定义解析器实现委托

这里我们用delegateToSchema工具,先获取Viewer实例,再以它为根对象委托调用目标字段:

const { delegateToSchema } = require('@graphql-tools/delegate');
const { mergeSchemas } = require('@graphql-tools/schema');

async function buildFinalSchema() {
  const remoteSchema = await getRemoteSchema();
  
  const resolvers = {
    Query: {
      // 迁移Viewer.user字段到Query
      user: async (_, args, context, info) => {
        // 第一步:获取远程Viewer实例
        const viewer = await delegateToSchema({
          schema: remoteSchema,
          operation: 'query',
          fieldName: 'viewer', // 远程Query上获取Viewer的字段名
          context,
          info,
        });
        
        // 第二步:以Viewer为父对象,委托调用它的user字段
        return delegateToSchema({
          schema: remoteSchema,
          operation: 'query',
          fieldName: 'user', // Viewer上的目标字段名
          args,
          context,
          info,
          rootValue: viewer, // 指定父对象为刚才获取的Viewer
          parentType: remoteSchema.getType('Viewer'), // 明确父类型是Viewer
        });
      },
      
      // 迁移Viewer.latestPosts字段到Query
      latestPosts: async (_, args, context, info) => {
        const viewer = await delegateToSchema({
          schema: remoteSchema,
          operation: 'query',
          fieldName: 'viewer',
          context,
          info,
        });
        
        return delegateToSchema({
          schema: remoteSchema,
          operation: 'query',
          fieldName: 'latestPosts',
          args,
          context,
          info,
          rootValue: viewer,
          parentType: remoteSchema.getType('Viewer'),
        });
      },
    },
  };

  // 合并远程Schema和本地扩展Schema
  return mergeSchemas({
    schemas: [remoteSchema, localExtensionSchema], // localExtensionSchema是你定义的扩展Schema
    resolvers,
  });
}

4. 封装通用工具函数简化代码

如果要迁移多个字段,重复写获取Viewer的逻辑会很冗余,我们可以封装一个通用函数:

async function delegateFromViewer(fieldName, args, context, info, remoteSchema) {
  const viewer = await delegateToSchema({
    schema: remoteSchema,
    operation: 'query',
    fieldName: 'viewer',
    context,
    info,
  });
  
  return delegateToSchema({
    schema: remoteSchema,
    operation: 'query',
    fieldName,
    args,
    context,
    info,
    rootValue: viewer,
    parentType: remoteSchema.getType('Viewer'),
  });
}

// 然后解析器就可以简化成:
const resolvers = {
  Query: {
    user: (_, args, context, info) => delegateFromViewer('user', args, context, info, remoteSchema),
    latestPosts: (_, args, context, info) => delegateFromViewer('latestPosts', args, context, info, remoteSchema),
  },
};

关键注意点

  • 确保远程Schema的Query类型确实存在viewer字段(如果没有,需要确认远程API如何获取Viewer实例,比如是否需要通过认证上下文自动关联)
  • 字段的参数、返回类型要和远程Viewer上的字段完全匹配,避免类型校验错误
  • 认证信息要正确通过context传递给远程Schema,否则可能无法获取到合法的Viewer实例

内容的提问来源于stack exchange,提问作者Andrew Safigan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.27 07:08:55