如何扩展远程GraphQL Schema,添加带id参数的Persons字段?
如何扩展导入的远程GraphQL Schema(JavaScript方案)
当然可以扩展你导入的远程Schema!用GraphQL生态里的Schema Stitching技术就能轻松实现给Persons字段添加id参数、返回单个Person的需求。下面是具体的实现步骤和代码示例:
1. 获取并包装远程Schema
首先我们需要把远程的Schema导入到本地,这里用introspection的方式获取远程Schema的结构,再包装成可在本地调用的Schema:
const { fetch } = require('cross-fetch'); const { buildClientSchema, introspectionQuery } = require('graphql'); const { wrapSchema, introspectSchema } = require('@graphql-tools/wrap'); const { stitchSchemas } = require('@graphql-tools/stitch'); // 替换成你的远程GraphQL服务器地址 const REMOTE_GQL_URL = 'https://your-remote-server.com/graphql'; async function getWrappedRemoteSchema() { // 通过introspection获取远程Schema的元数据 const introspectionResponse = await fetch(REMOTE_GQL_URL, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ query: introspectionQuery }), }).then(res => res.json()); // 构建远程Schema对象 const rawRemoteSchema = buildClientSchema(introspectionResponse.data); // 包装远程Schema,让我们能在本地调用它的字段 return wrapSchema({ schema: rawRemoteSchema, executor: async ({ document, variables }) => { const response = await fetch(REMOTE_GQL_URL, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ query: document, variables }), }); return response.json(); }, }); }
2. 定义扩展Schema与Resolver
接下来创建一个扩展Schema,给Persons字段添加id参数,同时编写Resolver处理新参数的逻辑——调用远程的全量Persons接口,再过滤出匹配id的Person:
const { makeExecutableSchema } = require('@graphql-tools/schema'); // 扩展的Schema定义:给Query下的Persons添加id参数,返回单个Person类型 const extensionTypeDefs = ` extend type Query { Persons(id: ID!): Person } `; // 扩展的Resolver逻辑 const extensionResolvers = { Query: { Persons: async (_, { id }, context) => { // 调用远程Schema的全量Persons接口 const remoteData = await context.remoteExecutor({ query: ` query FetchAllPersons { Persons { id name email # 这里列出你需要的所有Person字段 } } `, }); // 从全量数据中过滤出匹配id的单个Person return remoteData.data.Persons.find(person => person.id === id); }, }, }; // 创建可执行的扩展Schema const extensionSchema = makeExecutableSchema({ typeDefs: extensionTypeDefs, resolvers: extensionResolvers, });
3. 合并远程Schema与扩展Schema
用stitchSchemas把两个Schema合并,这样你的服务器就会同时支持两种调用方式:
- 无参数的
Persons:返回全部Person数据 - 带
id参数的Persons(id: "123"):返回单个匹配的Person
async function buildCombinedSchema() { const remoteSchema = await getWrappedRemoteSchema(); return stitchSchemas({ subschemas: [ { schema: remoteSchema, // 给扩展Resolver提供调用远程Schema的方法 context: ({ req }) => ({ remoteExecutor: async ({ query, variables }) => { const response = await fetch(REMOTE_GQL_URL, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ query, variables }), }); return response.json(); }, }), }, extensionSchema, ], }); }
4. 启动本地GraphQL服务器(以Apollo Server为例)
最后用Apollo Server托管合并后的Schema,就可以对外提供服务了:
const { ApolloServer } = require('apollo-server'); async function startServer() { const combinedSchema = await buildCombinedSchema(); const server = new ApolloServer({ schema: combinedSchema, }); const { url } = await server.listen({ port: 4000 }); console.log(`本地GraphQL服务器已启动:${url}`); } startServer();
性能优化提示
上面的方案是先获取全量Person再过滤,如果你觉得这种方式性能不佳,可以检查下远程Schema是否有隐藏的单个Person查询字段(比如Person(id: ID!))。如果有的话,直接在Resolver里调用这个字段会更高效,不用拉取全量数据。
另外,如果你希望带id参数时返回的还是数组类型(只包含单个匹配元素),只需要把扩展SDL里的返回类型改成[Person],Resolver里返回[remoteData.data.Persons.find(...)]即可。
内容的提问来源于stack exchange,提问作者Muhammad Kashif Nazar
相关产品推荐
相关产品推荐

