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

如何在Apollo嵌套查询中高效跨子图传递用户数据至折扣服务

问题:Apollo联邦中高效为discounts-service传递用户数据的方案

背景

现有联邦GraphQL Schema如下:

type Query {
    products: [Product]!
}

// resolved by `discounts-service`
type Discount {
    priceWithDiscount: Float
    expired: Boolean
}

// resolved by `pricing-service`
type Price @key(fields: "sku") @extends {
    sku: String!
    amount: Int!
    discount: Discount! @requires(fields: "amount")
}

// resolved by `products-service`
type Product @key(fields: "sku") {
    sku: String!
    title: String!
    price: Price!
}

查询逻辑为:返回多个Product,每个Product包含Price节点,Price又关联Discount节点。

现在需要让Discount与单个用户绑定,其解析器需要获取当前查询用户的id和segmentation属性,用户节点定义如下:

// resolved by `users-service`
type User @key(fields: "id") {
    id: String!
    name: String!
    segmentation: String!
    // ....
}

要求实现高效的方案:避免在Product/Price节点添加@external类型User这类朴素方案(会导致Apollo Router为每个产品重复调用users-service,造成资源浪费)。同时,也不采用给discount加参数的方案——该方案需要调用方先获取用户数据,且客户端传入的信息不可信(用户可能伪造segmentation)。


可行高效方案

1. 通过请求上下文(Context)传递用户身份信息

这是最直接且安全的方案:

  • 在**Apollo Router(网关)**层面完成用户身份解析:比如从请求头的JWT、Session等可信来源提取用户id和segmentation,将这些信息注入到每个子服务的请求上下文中。
  • discounts-service的Discount解析器直接从请求上下文中读取用户数据,无需通过Schema层级传递,也不会触发对users-service的重复调用。
  • 由于数据由网关解析后注入,客户端无法篡改,保证了数据可信度。

示例代码(伪代码):

  • 网关配置中处理认证并注入上下文:
// Apollo Router 自定义插件逻辑
export function plugin() {
  return {
    async requestDidStart() {
      return {
        async didResolveOperation({ request, context }) {
          // 从请求头提取JWT并解析
          const token = request.http.headers.get('Authorization')?.split(' ')[1];
          const userInfo = verifyToken(token); // 解析出id和segmentation
          // 将用户信息注入到共享上下文
          context.user = {
            id: userInfo.id,
            segmentation: userInfo.segmentation
          };
        }
      };
    }
  };
}
  • discounts-service的解析器读取上下文:
// discounts-service中Discount的解析器
const resolvers = {
  Price: {
    discount(parent, args, context) {
      const { id, segmentation } = context.user;
      const originalAmount = parent.amount;
      // 根据用户信息计算折扣
      return calculateDiscount(originalAmount, id, segmentation);
    }
  }
};

2. 基于联邦共享上下文的批量用户数据获取

如果用户信息无法直接从网关请求头解析,需要调用users-service获取,可采用此方案:

  • 在网关层面,针对当前查询**仅调用一次users-service**获取当前用户的完整数据,将其存入联邦共享上下文。
  • 后续所有子服务(包括discounts-service)都能从共享上下文中直接读取用户数据,无需重复调用users-service,无论返回多少Product,都只会触发一次用户数据查询。

这种方式既保证了效率,又确保用户数据来自可信服务,避免客户端伪造。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 14:18:36