从graphql中两个不相干的源解析为同一对象

时间:2019-06-18 10:21:41

标签: graphql apollo-client

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

我正在一个项目中,我们使用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,
}

我听说过一些关于阿波罗联合会的信息,但是我不能在我们系统的每个API前面都放置一个graphql服务器,所以我迷失了如何在数据传输时实现前端应用程序的透明性来自两个不同的来源。

如果有人已经遇到了相同的问题或对我可以做的事情有任何建议,我都会听到:)

1 个答案:

答案 0 :(得分:1)

您需要确定哪种类型的User类型的“形状”对您的客户端应用程序有意义,而与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的响应转换为适合您的模式。