【问题标题】:Resolve to the same object from two incoherent sources in graphql从graphql中的两个不相干源解析到同一个对象
【发布时间】:2019-10-31 23:17:43
【问题描述】:

我有一个问题我不知道如何正确解决。

我正在开发一个项目,我们使用 graphql 服务器与不同的 api 进行通信。这些 api 很旧而且很难更新,所以我们决定使用 graphql 来简化我们的沟通。

目前,两个 api 允许我获取用户数据。我知道这不连贯,但遗憾的是我无法对此进行任何更改,我需要将它们中的两个用于不同的操作。所以为了简单起见,我想从我的前端应用程序中抽象出来,所以它只要求用户数据,总是采用相同的格式,无论这些数据来自哪个 api。

只有一个 api,graphql 的解析器系统帮助很大。但是当我从第二个 api 访问用户数据时,我发现总是将同一个对象发送回我的首页非常困难。这两个 api,即使它们具有大部分相同的数据,也具有不同的响应格式。所以在我的解析器中,根据数据的来源,我应该做一件事或另一件事。

例子:

API A 

type User {
   id: string,
   communication: Communication
}

type Communication {
   mail: string,
}
API B

type User {
   id: string,
   mail: string,
}

我听说过一些关于 apollo-federation 的信息,但我不能在我们系统的每个 api 前面放置一个 graphql 服务器,所以我有点迷茫我如何在数据时为我的前端应用程序实现透明度来自两个不同的来源。

如果有人已经遇到同样的问题或对我能做的事情有建议,我都会听到:)

【问题讨论】:

    标签: graphql apollo-client


    【解决方案1】:

    无论 REST API 返回什么内容,您都需要确定对您的客户端应用有意义的用户类型“形状”。对于这个例子,假设我们使用:

    type User {
      id: String
      mail: String
    }
    

    此外,为了这个示例,假设我们有一个返回单个用户的getUser 字段。任何参数都与场景无关,所以我在这里省略它们。

    type Query {
      getUser: User
    }
    

    假设我不知道要为用户查询哪个 API,getUser 的解析器可能如下所示:

    async () => {
      const [userFromA, userFromB] = await Promise.all([
        fetchUserFromA(),
        fetchUserFromB(),
      ])
    
      // transform response
      if (userFromA) {
        const { id, communication: { mail } } = userFromA
        return {
          id,
          mail,
        }
      }
    
      // response from B is already in the correct "shape", so just return it
      if (userFromB) {
        return userFromB
      }
    }
    

    或者,我们可以利用单独的字段解析器来实现相同的效果。例如:

    const resolvers = {
      Query: {
        getUser: async () => {
          const [userFromA, userFromB] = await Promise.all([
            fetchUserFromA(),
            fetchUserFromB(),
          ])
          return userFromA || userFromB
        },
      },
      User: {
        mail: (user) => {
          if (user.communication) {
            return user.communication.mail
          }
          return user.mail
        }
      }, 
    }
    

    请注意,您不必将架构与来自现有 REST 端点的任一响应相匹配。例如,也许您想像这样返回一个用户:

    type User {
      id: String
      details: UserDetails
    }
    
    type UserDetails {
      email: String
    }
    

    在这种情况下,您只需转换来自任一 API 的响应以适应您的架构。

    【讨论】:

    • 感谢 Daniel,现在我使用带有 if 的单个字段解析器,它可以工作,但我想知道是否有更好、更强大的方法来做到这一点。我的例子在这里很简单,但实际上它是相当复杂的对象^^'
    • 不是特别的。如果标准化响应很复杂,您可能会发现将响应转换逻辑提取到一个单独的函数中更易于管理和可扩展,并且只需在每个返回用户或用户列表的解析器中调用该函数。这样,您就可以避免为 User 类型的各个字段提供解析器。
    • 您也可以使用抽象类型(联合和接口),但是创建单独的用户类型并使用抽象类型组合它们会将处理两个响应之间差异的负担从服务器转移到客户端,这可能不是您想要做的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-12
    相关资源
    最近更新 更多