基于GraphQL Schema Stitching,如何合并两个服务的查询结果为单个数组?
如何通过Schema Stitching合并两个GraphQL服务的查询结果?
当然可以实现。你需要在网关层为统一的search查询编写自定义解析器,同时调用两个子服务的_search接口,将返回结果合并后返回。
先明确各服务的Schema定义:
图书服务Schema
interface Searchable { id: ID! } type Book implements Searchable { id: ID! name: String # 其他字段 } type Query { _search( term: String ): [Searchable] }
用户服务Schema
interface Searchable { id: ID! } type Author implements Searchable { id: ID! name: String # 其他字段 } type Query { _search( term: String ): [Searchable] }
期望的网关Schema
interface Searchable { id: ID! } type Book implements Searchable { id: ID! name: String # 其他字段 } type Author implements Searchable { id: ID! name: String # 其他字段 } type Query { search( term: String ): [Searchable] }
实现步骤与代码示例
核心是利用@graphql-tools/stitch的自定义解析器能力,同时触发两个子服务的查询并合并结果:
- 导入子服务的远程Schema:先通过 introspect 获取两个子服务的Schema实例,并配置对应的执行器。
- 添加网关自定义查询与解析器:在合并Schema时,定义网关的
search查询,并编写解析器逻辑。
import { stitchSchemas } from '@graphql-tools/stitch'; import { introspectSchema } from '@graphql-tools/wrap'; import { fetch } from 'cross-fetch'; // 创建远程服务的请求执行器 async function createRemoteExecutor(serviceUrl) { return async ({ document, variables }) => { const response = await fetch(serviceUrl, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ query: document, variables }), }); return response.json(); }; } // 初始化图书服务的Schema与执行器 const bookServiceExecutor = createRemoteExecutor('http://your-book-service-url/graphql'); const bookServiceSchema = await introspectSchema(bookServiceExecutor); // 初始化用户服务的Schema与执行器 const authorServiceExecutor = createRemoteExecutor('http://your-author-service-url/graphql'); const authorServiceSchema = await introspectSchema(authorServiceExecutor); // 合并Schema并实现search查询的合并逻辑 const gatewaySchema = stitchSchemas({ subschemas: [ { schema: bookServiceSchema, executor: bookServiceExecutor }, { schema: authorServiceSchema, executor: authorServiceExecutor }, ], // 定义网关的自定义查询 typeDefs: ` extend type Query { search(term: String): [Searchable] } `, resolvers: { Query: { async search(_, { term }, context) { // 并行调用两个子服务的_search接口 const [bookSearchResult, authorSearchResult] = await Promise.all([ context.executors.get(bookServiceSchema)({ document: '{ _search(term: $term) { __typename id name } }', variables: { term }, }), context.executors.get(authorServiceSchema)({ document: '{ _search(term: $term) { __typename id name } }', variables: { term }, }), ]); // 合并两个服务的返回结果 return [ ...bookSearchResult.data._search, ...authorSearchResult.data._search ]; }, }, // 确保接口类型解析正确(若自动解析有问题可手动指定) Searchable: { __resolveType(obj) { return obj.__typename; }, }, }, });
关键注意点
- 两个子服务的
Searchable接口结构必须完全一致,否则Schema合并时会抛出兼容性错误; - 查询时必须请求
__typename字段,这样网关才能正确识别返回对象的具体类型,完成接口的类型解析; - 可根据业务需求添加错误处理逻辑,比如某个服务不可用时,返回另一个服务的结果,避免整体查询失败。
内容的提问来源于stack exchange,提问作者Jimish Fotariya
相关产品推荐
相关产品推荐

