如何从服务器加载graphql查询而不在前端定义它?

时间:2018-11-02 09:01:29

标签: graphql apollo

现在让我们说我们正在使用REST API。我有一个这样的端点:/homeNewsFeed。该API将给我们这样的响应:

[
  {
    blockTitle: 'News',
    type: 'list',
    api: 'http://localhost/news'
  },
  {
    blockTitle: 'Photos',
    type: 'gallery',
    api: 'http://localhost/gallery'
  }
]

现在,在获取此信息之后,我们遍历数组并调用相应的端点以加载数据。我的问题是,如何在GraphQL中做到这一点?通常,我们在前端代码中定义查询。不这样做,如何让服务器决定发送什么?

这样做的主要原因是。想象我们有一个移动应用程序。我们需要在不发送应用更新的情况下将新的内容推送到此新闻提要。但是每个项目都可以有自己的查询。

1 个答案:

答案 0 :(得分:4)

  

通常,我们在前端代码中定义查询。不这样做,如何让服务器决定发送什么?

对于spec,GraphQL执行请求必须包括两件事:1)模式; 2)包含操作定义的文档。操作定义确定要执行的操作(哪个查询或变异)以及响应的格式。有解决方法和例外(我将在下面讨论),但是,通常,如果在客户端指定响应的形式是不希望的,或者在某种程度上是不可能的,则应仔细考虑GraphQL是否是满足您需求的正确解决方案

此外,GraphQL更适合于单个请求,而不是像现有REST API所要求的一系列结构化请求。因此响应看起来更像这样:

[
  {
    title: 'News',
    content: [
      ...
    ],
  },
  {
    title: 'Photos',
    content: [
      ...
    ],
  }
]

,相应的查询可能如下所示:

query HomePageContent {
  blocks {
    title
    content {
      # additional fields
    }
  }
}

现在的问题变成了如何区分不同种类的content。通常可以通过利用接口或联合将多种类型聚合为单个抽象类型来解决此问题。模式的确切结构将取决于您发送的数据,但这是一个示例:

interface BlockContentItem {
  id: ID!
  url: String!
}

type Story implements BlockContentItem {
  id: ID!
  url: String!
  author: String!
  title: String! 
}

type Image implement BlockContentItem {
  id: ID!
  url: String!
  alt: String!
}

type Block {
  title: String!
  content: [BlockContentItem!]!
}

type Query {
  blocks: [Block!]!
}

您现在可以像这样查询blocks

query HomePageContent {
  blocks {
    title
    content {
      # these fields apply to all BlockContentItems
      __typename
      id
      url
      # then we use inline fragments to specify type-specific fields
      ... on Image {
        alt
      }
      ... on Story {
        author
        title
      }
    }
  }
}

像这样使用内联片段可确保仅针对那些类型的实例返回特定于类型的字段。我加入了__typename来确定给定对象的类型,这可能对客户端应用程序有帮助(反正Apollo之类的客户端会自动包括此字段)。

当然,当您要添加新块时,仍然会发生问题。如果块的内容适合现有类型,则不费吹灰之力。但是,当您预计将来会需要另一种类型,但现在不能围绕这种类型进行设计时,会发生什么呢?

通常,这种更改将需要服务器上的架构更改和客户端上的查询更改。在大多数情况下,这可能会很好,因为如果您以不同的结构获取数据,则您仍然必须更新客户端应用。否则,您的应用程序将不知道如何正确呈现新的数据结构。

但是,可以说我们无论如何都要对模式进行过时的验证。这是您可以执行此操作的两种方法。

  1. 无需为content指定接口,只需利用自定义JSON标量即可。这样可以有效地将响应验证排除在窗口之外,但可以让您返回给定块内容的任何内容。

  2. 将将来可能需要的任何字段抽象为某种值键类型。例如:

type MetaItem {
  key: String!
  value: String!
}

type Block {
  title: String!
  meta: [MetaItem!]!
  # other common fields
}

还有许多其他变通办法,根据您使用的数据类型,一些变通办法比其他变通办法更好。但是希望可以使您有所了解,如何解决在GraphQL上下文中描述的情况。