GraphQL:一种更高效、强大和灵活的数据提供方式

在前几天的 《StateOfJS: 2018年JavaScript生态圈趋势报告》一文中,咱们看到了2018年在数据层GraphQL的发展势头猛烈,而且大部分用户用过都说好,但如上图数据显示,目前国内的使用人数还不多,大部分人连听都没听过,今天小肆就为你们介绍一下,何为GraphQL。

一. GraphQL为什么会出现?

当提起API设计的时候,你们一般会想到SOAP,RESTful等设计方式,从2000年RESTful的理论被提出的时候,在业界引发了很大反响,由于这种设计理念更易于用户的使用,因此便很快的被你们所接受。咱们知道REST是一种从服务器公开数据的流行方式。前端

当REST的概念被说起出来时,客户端应用程序对数据的需求相对简单,而开发的速度并无达到今天的水平。react

所以REST对于许多应用程序来讲是很是适合的。然而在业务愈加复杂,客户对系统的扩展性有了更高的要求时,API环境发生了巨大的变化。特别是从下面三个方面在挑战api设计的方式:web

1. 移动端用户的爆发式增加须要更高效的数据加载

Facebook开发GraphQL的最初缘由是移动用户的增长、低功耗设备和松散的网络。GraphQL最小化了须要网络传输的数据量,从而极大地改善了在这些条件下运行的应用程序。数据库

2. 各类不一样的前端框架和平台

前端框架和平台运行客户端应用程序的异构环境使得咱们在构建和维护一个符合全部需求的API变得困难,使用GraphQL每一个客户机均可以精确地访问它须要的数据。segmentfault

3. 在不一样前端框架,不一样平台下想要加快产品快速开发变的愈来愈难

持续部署已经成为许多公司的标准,快速的迭代和频繁的产品更新是必不可少的。对于REST api,服务器公开数据的方式经常须要修改,以知足客户端的特定需求和设计更改。这阻碍了快速开发实践和产品迭代。后端

二. GraphQL官方定义:一种用于 API 的查询语言


GraphQL 既是一种用于 API 的查询语言也是一个知足你数据查询的运行时。 GraphQL 对你的 API 中的数据提供了一套易于理解的完整描述,使得客户端可以准确地得到它须要的数据,并且没有任何冗余,也让 API 更容易地随着时间推移而演进,还能用于构建强大的开发者工具。api

请求你所要的数据:很少很多

向你的 API 发出一个 GraphQL 请求就能准确得到你想要的数据,很少很多。 GraphQL 查询老是返回可预测的结果。使用 GraphQL 的应用能够工做得又快又稳,由于控制数据的是应用,而不是服务器。数组

获取多个资源:只用一个请求

GraphQL 查询不只可以得到资源的属性,还能沿着资源间引用进一步查询。典型的 REST API 请求多个资源时得载入多个 URL,而 GraphQL 能够经过一次请求就获取你应用所需的全部数据。这样一来,即便是比较慢的移动网络链接下,使用 GraphQL 的应用也能表现得足够迅速。前端框架

描述全部的可能:类型系统

GraphQL API 基于类型和字段的方式进行组织,而非入口端点。你能够经过一个单一入口端点获得你全部的数据能力。GraphQL 使用类型来保证应用只请求可能的数据,还提供了清晰的辅助性错误信息。应用可使用类型,而避免编写手动解析代码。服务器

三. GraphQL和RESTful的区别

前面提到GraphQL能够理解为基于RESTful的一种封装,目的在于构建使Client更加易用的服务,能够说GraphQL是更好的RESTful设计。

在过去的十多年中,REST已经成为设计web api的标准(虽然只是一个模糊的标准)。它提供了一些很棒的想法,好比无状态服务器和结构化的资源访问。

然而REST api表现得过于僵化,没法跟上访问它们的客户的快速变化的需求。 GraphQL的开发是为了应付更多的灵活性和效率,它解决了与REST api交互时开发人员所经历的许多缺点和低效之处。

为了说明在从API获取数据时REST和GraphQL之间的主要区别,让咱们考虑一个简单的示例场景:在blog应用程序中,应用程序须要显示特定用户的文章的标题。同一屏幕还显示该用户最后3个关注者的名称。

REST和GraphQL如何解决这种状况?

使用REST API来现实时,咱们一般能够经过访问屡次请求来收集数据。

好比在这个示例中,咱们能够经过下面的三步来实现:

  1. 经过 /user/<id>获取初始用户数据
  2. 经过/user/<id>/posts 返回用户的全部帖子
  3. 请求/user/<id>/followers返回每一个用户的关注者列表

调用关系以下图所示:

若是用GraphQL的话,咱们只须要一次请求就能够完成上述的需求

在GraphQL的世界里咱们不用多取数据,也不用担忧数据取少了,咱们只须要按需获取便可。

REST最多见的问题之一是API的返回数据过多或者过少,这是由于客户端下载数据的惟一方法是经过访问返回固定数据结构的endpoint,这就会致使咱们设计API很是困难,由于它既要可以为客户提供精确的数据需求,又须要知足不一样调用者的需求,这自己就是相互矛盾的。GraphQL的发明者Lee Byron提出了一个很重要的概念: “用图形来思考,而不是endpoint”

经过上述直观展现咱们能够得出一下几点:

1. 获取了许多多余的数据

一般状况下咱们在调用一个通用API接口时,客户端获取的信息比应用程序中实际须要的要多。例如UI须要显示一个用户列表,而实际上只须要使用他们的名字。在REST API中一般会调用 /user 这个endpoint,并接收一个带有用户数据的JSON数组。可是这个响应可能包含更多关于返回的用户的信息,例如他们的生日或地址,而这些信息对客户来讲是无用的,由于它只须要显示用户的名字。

2. 获取的数据少于Client所须要的数据

通常来讲数据获取不足意味着某个特定的endpoint没有提供客户端须要的足够信息,客户端将须要额外的请求来获取它所须要的一切。这可能会升级到客户端须要首先获取列表信息,而后须要对单条数据添加一个额外的请求以获取其余所需的数据。

3. 前端的快速产品迭代对API有很大的挑战

REST api的一个常见模式是根据您在应用程序内部的展示逻辑来构造endpoint,这很方便,由于它容许客户端经过访问相应的endpoint获取特定视图的全部所需信息。

这种方法的主要缺点是它不容许前端的快速迭代。对于UI所作的每个更改,如今都存在比之前更多(或更少)的数据的高风险。

所以,须要对后端进行调整,以知足新的数据需求,这会下降生产力并显著下降将用户反馈集成到产品中的能力。 使用GraphQL这个问题就解决了。

因为GraphQL的灵活性,无需在服务器上额外工做就能够在客户端上进行更改。因为客户端能够指定准确的数据需求,因此当前端的设计和数据需求发生变化时,并不须要后端API作出任何的修改就能够知足展示层的变化。

4. Schema和类型系统的好处

GraphQL使用强大的Type System来定义API的功能。全部在API中公开的类型都是使用GraphQL schema Definition Language (SDL)在模式中编写的。

该模式充当客户端和服务器之间的契约,以定义客户机如何访问数据。 一旦定义了模式,在前端和后端工做的团队就能够在没有进一步通讯的状况下完成工做,由于他们都知道经过网络发送的数据的确切结构。

前端团队能够经过mock所需的数据结构来轻松测试他们的应用程序。一旦后端API实现完成,就能够对客户端应用程序进行切换来调用实际的API获取数据,这也可使得咱们实现更好的客户端和服务端的分离。

四. GraphQL语法

基础语法

其实GraphQL所须要学习的语法不多,大部分语法与咱们平时的语法一致,能够经过官网详细了解。

首先,GraphQL是一门强类型语言,因此和咱们在数据库定义一张表同样,咱们须要定义每个属性的类型.以下图所示:

下面是一个简单的类型定义,先是定义了一个枚举,而后咱们定义了一个类型,类型中有四个属性:id、 name、 friends、 appearsIn,其中id和name是标量类型,而friends是一个Person类型,这是一个嵌套类型,仔细想一想应该没什么毛病,毕竟你的朋友和你同样,都是人,而appearsIn是一个枚举类型,看起来仍是很熟悉的。

enum Episode {
  NEWHOPE
  EMPIRE
  JEDI
}
type Person{
     id: ID! 
     name: String!
     friends: [Person]
     appearsIn: [Episode]! 
}

了解完类型,再了解一下Arguments和resolver,二者都是偏服务端一些,可是了解一下,对graphql的使用原理有进一步的认识。

对于一个Restful API来说,除了知道接口URL,咱们还须要知道接口的传参定义,对于GraphQL其实也同样,虽然URL只有一个,不一样的接口经过type来区别,但传参同Restful API同样,体现了客户端与服务端的交互。

好比下面,查询的目标是id = 2的用户,获取他的用户名:

Query{
    user(id: 2) {
        id
        userName
    }
}

而在服务端定义一个接口时,咱们也须要去定义入参,主要从两个方面,一是类型,二是其是否必填,好比下面这样:

接口定义

user: {
    type: UserType,
    args: {
      id: { type: GraphQLID }
    },
    resolve: (root, args, context, info) => {
      const { id } = args;
      return getUser(id);
    }
  }

查询定义

上面的代码只是定义了一个输入属性id,并未定义其是不是必填,因此当查询时,若是没有配置查询id,查询不会报错,只会获取一个为null的空值结果。可是讲道理的话,咱们但愿这是一个必填项,因此咱们须要修改服务端的代码,将id: { type: GraphQLID } 更换为id: {type: new GraphQLNonNull(GraphQLID)},这句代码的含义就表示id是一个类型为ID的必填项,再次执行咱们的查询能够获得下面的错误提示,提示id是一个必填的ID类型,同时右侧也没有获取到为空的查询结果.

在讲上面Arguments时候,能够零星的看到type中有一个resolve方法,其接收root, args, context, info四个参数。

其中root表明这个type上父节点的resolve值(由于GraphQL支持嵌套查询),args就是上面讲的,context表在Resolver解析链中不断传递的中间变量,和react的上下文类似,而info这个概念,是当前Query的AST对象,比较抽象,可是能够经过查看info,获取这个QUERY的编译对象。这个方法也是后端服务编写的重点部分,经常咱们能够在这里与已有的Restful API关联起来。

核心概念

Schema能够说是GraphQL最具核心的部分,其描述了整个接口向外暴露的形式。

像Restful API,咱们会定义一个查询全部人的接口url定义为:
/api/v1/user/getUsers

查询人具体信息的接口url为:
/api/v1/user/getUserById

新增一我的员的接口url定义为:
/api/v1/user/createUser

这样前端人员调用起来会很直观。

可是graphql是彻底不同的使用方式,其向前端暴露的url就一个像/api/graphql之类的,那这么多接口怎么区分呢? 咱们来看看:

奥妙就是上面这张图,一个graphql接口都有一个Schema定义。

其定义三种操做方式:query(查询),mutation(变动)和subscription(监听)。

再往下延伸,一个查询中包含多个field,也就是多种不一样的查询,好比query user查询人,query message查询消息,query weather查询天气。

经过这些就实现了Restful API使用多个url来达到不一样操做的效果。

总结:

今天咱们只是讲了一些GraphQL的基本知识,但咱们依然能够看出GraphQL的出现可使咱们后端API具备更大的灵活性以及扩展性,知足了不一样client对数据的须要,大大丰富了API的数据提供的能力。

部份内容来源:https://segmentfault.com/a/11...

相关文章
相关标签/搜索