GraphQL简介

原文地址 https://flaviocopes.com/graphql/
中译文地址

什么是GraphQL

GraphQL是API技术的新前沿。

它是一种API查询语言,以及一组用于执行查询的服务器端运行时(服务端可以以各种后端语言实现)。

它与特定技术无关,你可以用任何语言实现它。

它是一种直接与REST(REpresentational State Transfer)API 竞争的新方案,就像REST 之前与SOAP竞争一样。

GraphQL是在Facebook上开发的,就像最近震撼JavaScript世界的许多技术一样,如React和React Native,它在2015年公开发布 - 尽管Facebook在内部使用它几年。

除了Facebook,许多大公司都在采用GraphQL,包括GitHub,Pinterest,Twitter,Sky,纽约时报,Shopify,Yelp等数千家公司。

GraphQL的原则

GraphQL只公开单个端点

你使用特殊的查询语法向该端点发送查询。该查询只是一个字符串

服务器使用JSON对象来响应查询。

下面让我们看一下这种查询的第一个例子。此查询获取id=1的人员姓名:

GET /graphql?query={ person(id: "1") { name } }

或者:

{
  person(id: "1") {
    name
  }
}

我们将收到如下的JSON响应:

{
  "name": "Tony"
}

让我们增加一点复杂性:获取某个人的姓名和所在城市,其中城市从address对象中提取。我们不关心地址的其他细节,因为我们没有请求它们,服务器也不返回,

GET /graphql?query={ person(id: "1") { name, address { city } } }

或者

{
  person(id: "1") {
    name
    address {
      city
    }
  }
}

{
  "name": "Tony",
  "address": {
    "city": "York"
  }
}

综上所述,我们获得的数据与我们发送的请求的结构基本相同,并且填充了所获取的值。

GraphQL vs REST

由于REST是构建API的一种流行方法,并且比GraphQL更广泛,我们假设你已经熟悉了,让我们看看GraphQL和REST之间的差异。

Rest是一个概念

REST是一个事实上的架构标准,但它实际上并没有规范和大量的非官方定义。GraphQL有一个规范草案,它是一种查询语言而不是一种体系结构,围绕着它构建了一套定义良好的工具(以及一个繁荣的生态系统)。

虽然REST建立在现有架构之上,而在最常见的场景中是HTTP,但另一方面,GraphQL正在构建自己的约定。这可能是一个优势点,因为REST通过在HTTP层上缓存而免费获益。

单个端点

GraphQL只有一个端点,你可以在其中发送所有查询。如果使用REST方法,你可以创建多个端点,并使用HTTP 动词来区分读操作(GET)和写操作(POSTPUTDELETE)。GraphQL不使用HTTP动词来确定请求类型。

根据你的需求量身定制

使用REST,你通常无法选择服务器返回给你的内容,除非服务器使用稀疏字段集实现部分响应,并且客户端使用该功能。API维护者无法强制执行此类过滤。

API通常会向你返回比你需要的信息更多的信息,除非你也控制API服务器,并为每个不同的请求定制响应。

使用GraphQL,您可以明确地请求您需要的信息,您不能从完整的响应默认值中“选择退出”,只会强制选择您想要的字段。

这有助于节省服务器上的资源,因为你可能需要较少的处理流程以及很少的带宽,因为要传输的有效负载较小。

GraphQL可以轻松监控字段的使用情况

使用REST,除非强制使用稀疏字段集,否则无法确定客户端是否使用了字段,因此在进行重构或弃用时,无法确定实际使用情况。

GraphQL可以跟踪客户端使用的字段。

访问嵌套数据资源

GraphQL允许生成少得多的网络调用。

我们举个例子:你需要访问一个人的朋友的名字。如果你的REST API公开了一个/person端点,该端点返回一个带有朋友列表的人物对象,你通常首先通过执行获取人员信息GET /person/1,其中包含其朋友的ID列表。

除非这个人的朋友列表已经包含朋友姓名,否则有100个朋友需要向/person端点发出101个HTTP请求,这是一个巨大的时间成本,也是一个资源密集型操作。

使用GraphQL,你只需要一个请求,该请求会询问这个人的朋友的姓名。

类型

REST API基于JSON,无法提供类型控制。GraphQL有一个Type System

哪一个更好?

世界各地的组织正在质疑他们的API技术选择,他们正试图找出从REST迁移到GraphQL是否最适合他们的需求。

当你需要公开复杂的数据表示,以及客户端可能只需要数据的子集,或者他们定期执行嵌套查询以获取所需数据时,GraphQL非常适合。

与编程语言一样,没有单一的赢家,这完全取决于你的需求。

GraphQL查询

在本文中,你将了解GraphQL查询是如何组成的。

我将介绍的概念是

  • 字段和参数
  • 别名
  • 片段

字段和参数

下面是一个简单的GraphQL查询:

{
  person(id: "1") {
    name
  }
}

在此查询中,你会看到2个字段和1个参数。

该字段person返回一个Object,其中包含另一个字段类型是String

参数允许我们指定要引用的人。我们传递了一个id,如果我们交互的接口提供了通过名字来查找人的功能,我们也可以传递一个名字参数。

参数不限于任何特定字段,我们可以通过limit参数,指定希望API返回的朋友数量:

{
  person(id: "1") {
    name
    friends(limit: 100)
  }
}

别名

你可以要求API返回具有别名的字段,例如:

{
  owner: person(id: "1") {
    fullname: name
  }
}

将返回

{
  "data": {
    "owner": {
      "fullname": "Tony"
    }
  }
}

此功能除了为客户端代码创建更多临时命名外,如果你需要在同一查询中引用同一端点2次,这是唯一可以使用的方法:

{
  owner: person(id: "1") {
    fullname: name
  }
  first_employee: person(id: "2") {
    fullname: name
  }
}

片段

在上面的查询中,我们重复使用了person结构。片段允许我们指定结构(对许多字段非常有用):

{
  owner: person(id: "1") {
    ...personFields
  }
  first_employee: person(id: "2") {
    ...personFields
  }
}

fragment personFields on person {
  fullname: name
}

GraphQL变量

更复杂的GraphQL查询需要使用变量,使用变量可以动态指定查询中使用的

在这种情况下,我们在查询中添加了person id作为字符串:

{
  owner: person(id: "1") {
    fullname: name
  }
}

id很可能在我们的程序中动态改变,所以我们需要一种方法来传递它,而不是字符串插值

使用变量,可以将相同的查询写为

query GetOwner($id: String) {
  owner: person(id: $id) {
    fullname: name
  }
}

{
  "id": "1"
}

在此代码段中,我们已将GetOwner名称分配给查询。可以把它想象成命名函数,而前面的查询类似一个匿名函数。当你的应用程序中有大量查询时,命名查询很有用。

带有变量的查询定义看起来像一个函数定义,工作方式也很相似

必选变量

附加!到类型:

query GetOwner($id: String!)

只需要在类型后面附加!就能指定该字段为必填字段

指定变量的默认值

你可以使用以下语法指定默认值:

query GetOwner($id: String = "1")

GraphQL指令

指令可以根据变量为true或false来包含或排除字段。

query GetPerson($id: String) {
  person(id: $id) {
    fullname: name,
    address: @include(if: $getAddress) {
      city
      street
      country
    }
  }
}

{
  "id": "1",
  "getAddress": false
}

在这种情况下,如果getAddress我们传递的变量为true,我们也会得到地址字段,否则不会。

我们有两个指令可用:include(如果为真则包括),skipinclude相反(如果为true,则排除)

@include(if:Boolean)

query GetPerson($id: String) {
  person(id: $id) {
    fullname: name,
    address: @include(if: $getAddress) {
      city
      street
      country
    }
  }
}

{
  "id": "1",
  "getAddress": false
}

@skip(if:Boolean)

query GetPerson($id: String) {
  person(id: $id) {
    fullname: name,
    address: @skip(if: $excludeAddress) {
      city
      street
      country
    }
  }
}

{
  "id": "1",
  "excludeAddress": false
}

posted @ 2019-05-07 13:39  FeMiner  阅读(2009)  评论(0编辑  收藏  举报