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

Apollo Server 3如何为GraphQL响应添加元数据(装饰响应)

在Apollo Server 3中返回额外元数据的两种实现方案

方案一:根响应外层添加metadata(对应格式一)

Apollo Server 3 内置的 formatResponse 钩子是修改响应结构的官方方案,完全匹配你在根响应外层加metadata的需求:

  1. 步骤1:在Context中存储元数据
    在创建ApolloServer实例时,通过context函数生成或传入需要返回的元数据(比如请求ID、时间戳等):
const server = new ApolloServer({
  typeDefs,
  resolvers,
  context: ({ req }) => {
    // 根据请求上下文生成元数据
    const metadata = {
      requestId: req.headers['x-request-id'] || crypto.randomUUID(),
      timestamp: Date.now()
    };
    return { metadata };
  },
  // 步骤2:用formatResponse修改响应结构
  formatResponse: (response, { context }) => {
    // 组合原响应数据与元数据,保留错误字段(如果有)
    const formattedResponse = {
      metadata: context.metadata,
      data: response.data
    };
    if (response.errors) {
      formattedResponse.errors = response.errors;
    }
    return formattedResponse;
  },
});

启动服务后,客户端收到的响应就会是你需要的格式一结构。

方案二:在data对象内添加metaData(对应格式二)

这种方案需要调整Schema结构,让元数据成为Query类型的一个字段,或者通过解析器包装自动注入:

方式1:显式添加Query字段(推荐,符合GraphQL规范)

  1. 修改Schema
    在根Query类型中新增metaData字段,并定义对应的元数据类型:
type MetaData {
  requestId: String!
  timestamp: Int!
}

type Query {
  typeOne: TypeOne!
  typeTwo: TypeTwo!
  metaData: MetaData!
}
  1. 编写元数据解析器
    从Context中取出预存的元数据返回:
const resolvers = {
  Query: {
    typeOne: () => { /* 原typeOne解析逻辑 */ },
    typeTwo: () => { /* 原typeTwo解析逻辑 */ },
    metaData: (_, __, { metadata }) => metadata,
  },
};

客户端只需在查询中包含metaData字段,就能在返回的data对象中拿到它,完全符合格式二的结构。

方式2:自动注入metaData(无需客户端显式查询)

如果要求必须自动返回metaData,无需客户端额外查询,可以通过包装根Query解析器实现:

// 定义解析器包装函数,自动合并元数据到返回结果
const wrapRootResolver = (originalResolver) => async (parent, args, context, info) => {
  const resolveResult = await originalResolver(parent, args, context, info);
  // 仅当返回结果为对象时合并元数据
  if (typeof resolveResult === 'object' && resolveResult !== null) {
    return { ...resolveResult, metaData: context.metadata };
  }
  return resolveResult;
};

// 给所有根Query字段应用包装器
Object.keys(resolvers.Query).forEach(fieldName => {
  resolvers.Query[fieldName] = wrapRootResolver(resolvers.Query[fieldName]);
});

注意:这种方式要求所有根Query字段返回对象类型,否则会出现结构异常,仅适合特定业务场景。


内容的提问来源于stack exchange,提问作者Robert Moskal

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 06:15:17