【问题标题】:Pass through GraphQL variables to second function in an elegant manner以优雅的方式将 GraphQL 变量传递给第二个函数
【发布时间】:2018-09-26 15:28:31
【问题描述】:

我正在使用 GraphQL,但在寻找将变量从查询传递到结果的最佳方式时遇到了一些麻烦。

我有这样的架构:

type Fragment {

# The id of the fragment

  id: String!

# The key of the fragment
  key: String!

# The type of component
  component_type: String!

# The params used to build the fragment
  params: JSON

# Component data
  data: JSON

  children: [JSON]

  items: [JSON]


}

片段是指“cms”片段。解决此问题后,我想将一些查询数据传递到另一个后端。

我的查询如下所示:

  query getFragmentsWithItems($keys: [String!]! 
                     $platform: PlatformType 
                     $version: String
                     $userInfo: UserInput
                     $userId: Int
                     ) {                     
    fragmentsWithItems(keys: $keys, platform: $platform, version: $version, userInfo: $userInfo, userId: $userId) {
      key
      data 
      children
      params
      items
    }
  }

问题是:我在 Fragment 的 data 字段中有一些查询数据。在Fragment 解决之前,该数据不可用。我想获取该数据并将其发送到不同的后端。我想用 GraphQL 来做这件事,我希望做这样的事情:

Fragment: () => {
  async query(obj, args, context, info, {modles}) => {
    const items = await models.getItems(obj.query_string);
  }
}

但我需要传递给原始查询的user_infouser_id。显然,这只能从不打算使用的 info 参数访问。

我采取的另一条路径是使用手动解析器来执行以下操作:

const resolveFI = ({ keys, platform, version, userInfo, userId, models }) => {
  if (!keys || !keys.length) {
    return Promise.resolve(null);
  }
  return models.release.get({ platform, version }).then(release =>
    Promise.all(
      keys.map(key =>
        models.fragments.get({
          key,
          platform,
          version,
          release: release.id
        })
      )
    ).then(data => {
      const promises = [];
      data.rows.forEach(r => {
        if (r.data.query_data) {
          const d = {
            // Can just ignore
            filters: r.data.query_data.filters || {},
            user_info: userInfo,
            user_id: userId
          };
          promises.push(
            new Promise(resolve => {
              resolve(
                models.itemSearch.get(d).then(i => ({ items: i.items, ...r }))
              );
            })
          );
        }
        ...etc other backends 

这可行,但是手动承诺链似乎违背了使用 GraphQL 的目的。

我尝试的最后一件事是将items 设为非标量类型,类似于:

type Fragment {
items: ItemSearchResult(user_info: UserInput) etc

但由于我无法将 Fragment 中的实际结果通过管道传输到不起作用的 ItemSearchResult。

我意识到这很啰嗦,所以我愿意编辑或澄清。
我正在寻找我是否错过了更好的方法,或者我是否应该将其打包并让客户端应用在获取 Fragment 数据后执行项目查询。

【问题讨论】:

  • 您说“我想获取该数据并将其发送到不同的后端”-您是在尝试通过调用另一个 API 来解析 items 字段,还是只是尝试发布部分的查询结果到另一个后端?
  • 第二个。我想将查询 Fragments 得到的信息发布到项目后端。

标签: graphql


【解决方案1】:

并不是说你不应该使用info——这只是使用起来非常痛苦;)说真的,它是用于优化和更高级的用例,所以你应该如果没有更好的解决方案,请毫不犹豫地使用它。有一些库 (like this one) 可以用来更轻松地解析对象。

也就是说,我想有几种方法可以处理这个问题:

1.) 在您的查询解析器中

getFragmentsWithItems: async (obj, args, ctx, info) => {
  const fragments = await howeverYouDoThat()
  const backendCalls = fragments.map(fragment => {
    // extract whatever data you need from the fragment
    return asyncCallToBackEnd()
  })
  await backendCalls
  return fragments
}

不幸的是,如果您有很多不同的查询返回片段,最终会出现冗余。

2.) 在 Fragment 类型的现有字段(或附加字段)的解析器中。

如果你走这条路,并且你需要将参数传递给查询字段,你可以使用信息提取它们。或者,您也可以在查询解析器中改变上下文对象并将这些参数附加到它。然后,查询解析器“下方”的所有解析器(例如片段字段的解析器)都可以通过上下文访问这些参数。

3.) Apollo Server 允许您在配置其中间件时定义formatResponse 函数。这本质上提供了一个钩子,可以在响应返回给客户端之前对它做任何你想做的事情。您可以解析该函数​​内部的响应并从那里调用另一个后端。

【讨论】:

  • 感谢 cmets 和详细信息。由于我没有修改info,我想这将是最好的解决方法。我输入得太快了,我认为this package 是我一直在寻找的东西。它保留原始参数,直到其中一个解析器返回“未定义”。
猜你喜欢
  • 2014-09-17
  • 2017-11-10
  • 1970-01-01
  • 1970-01-01
  • 2014-10-03
  • 1970-01-01
  • 1970-01-01
  • 2011-03-08
  • 1970-01-01
相关资源
最近更新 更多