GraphQL使用总结

GraphQL是Facebook提出的一种数据查询规范,是一种用于描述CS应用中数据模型的能力和要求的语言。通过GraphQL可以使服务端仅通过一个API就可以满足所有的数据查询/修改需要,也允许客户端在不经过服务端允许的情况下,自由设定返回的数据。

与Restf API的区别

根据我的使用经验来说,90%的Restful API适应场景都可以被GraphQL所替代。 我们先说那10%不能替代的场景。

  • 需要上传/下载文件的场景

    如果有的场景通过Restful API来接受文件或者提供文件下载。 那么GraphQL无法满足。 因为GraphQL规定所有的数据都是通过body以json格式进行数据传输。 因此需要传输二进制数据的场合,GraphQL无法满足。 当然这不是最终结论,如果真要通过GraphQL满足这个场景,也可以将二进制数据进行Decode,然后作为字符串传输给对方。 可这么做费力不讨好,除了炫技没有什么好处。 所以这种需要不建议使用GraphQL。

  • 需要返回错误码的场景

    某些场景,例如用户登录。 是需要通过返回码来判断操作是否成功,此时GraphQL仍然无法满足。 因此GraphQL每个操作都是返回200,如果操作过程中出现了错误,也是放在error属性中,作为response的一部分返回给客户端。所以如果非要使用GraphQL,那么就需要客户端通过解析error属性来判断操作是否成功。 因此,遇到这种场景,也是不建议使用GraphQL。

  • 高并发的场景

    严格意义上面来说,这不是GraphQL的问题,而是背后架构和DB的问题。 只不过如果使用GraphQL以后,这个问题会集中爆发。 因为GraphQL中每个filed都对应一段业务逻辑操作(很大程度上是DB操作),因此遇到高并发以后,会产生大量的DB操作。 使用Restful API也有可能遇到这个问题,但Restful API可以通过增加缓存或者服务降级来规避掉,而GraphQL很不幸,还没有针对特定filed降级的方案(也可能有,但还没学习到)。

从我使用过程中,以上三种环境不建议使用GraphQL,其余Restful API的场景都可以通过GraphQL来提到。

痛点

结合业界的最佳实践,使用GraphQL之后可以解决下面三个痛点:

  • 显式获取数据

    客户端可以通过一次网络调用获取到所有预期(定制)的数据。 如果使用Restful API,则需要多次调用才能实现。

  • 摆脱服务端依赖

    客户端可以最大程度的消除服务端对API参数以及调用方式的依赖。 从某种意义上来说,API层对客户端实现了透明,客户端可以直接操作底层数据。

  • 定制数据

    客户端可以根据自身业务逻辑,快速方便的裁剪数据大小以及定制所需要的数据属性。

了解

使用GraphQL之前,首先了解一下它。

GraphQL 示意图

图中的GraphQL Server是一个请求解释器,充当了翻译客户端请求的角色。 在GraphQL Server中,定义了基于图的数据schema,并具备"发现"数据的能力(发现指的是,服务端可以通过某种方式查询、修改数据)。

图中的filed1,filed2,filed3分别表示客户端需要获取的三个字段属性, 从上图可以看出,每个字段都是并行执行的。对字段的处理,也不一定是DB操作,可以是其它计算。但实际使用中,以DB操作最为典型。

一个标准的GraphQL接口,只需要暴露出一个API。以下为了描述简单,我们称之外:/api。 假设当前服务器IP:127.0.0.1,端口8000。 那么GraphQL Endpoint就是: http://127.0.0.1:8000/api.

GraphQL接口通常会包含一个schema,用来描述每个字段的含义。 结合最佳实践来说,这个schema最好和数据库schema保持一致。 例如:我们定义一个用户User的schema(假设数据库中User的schema也是下面的属性定义),

type User struct{  
  Name    string
  Age     int
  Address string
  School  string
  Phone   string
  Area    string
}

GraphQL的工作就是设定好对每个字段如何处理,当收到User的操作请求时,按照设定好的处理方式并行处理每个字段请求。

字段

字段是GraphQL最核心的概念了。 GraphQL从思想上来说,就是将数据透明化的映射给了调用方。 这句话,如何理解? 我们假设现在有一个Restful API : /queryUser/:area. 这个API返回指定区域的用户信息。 服务端在v1时,返回了Name和Age。 而后调用方(假设是前端)反馈说,需要增加Address字段。好,又增加了Address字段。

随着开发的深入,前端有提出需要增加一个API,/queryUesr/:age,用来返回指定年龄的用户信息。 这个时候,后端除了新增API之外毫无办法。

如果使用GraphQL情况怎大不一样。 首先后端维护好User的schema。然后告诉前端,当前/api中User的schema是什么样子的。 如果前端需要查询指定area的用户信息(name,school字段),就可以通过:

query($area:String){  
  user(area:$area){
    name
    school
  }
}

request body:

{
  "area": "beijing" 
}

当前端需要通过age来获取用户信息,那么前端只需要修改请求参数:

query($age:Int){  
  user(age:$age){
    name
    school
  }
}

request body:

{
  "age":18 
}

想增加一些返回字段时怎么办? 很简单,直接添加就可以:

query($age:Int){  
  user(age:$age){
    name
    school
    phone
  }
}

request body:

{
  "age":18 
}

在这个过程中,后端什么都没有做。 前端可以自由定制返回的字段,需要哪些就获取哪些,不需要的一个都不要。

这只是字段对于客户端重要的一个方面,字段对于服务端同样重要。 在GraphQL示意图中可以看到,服务端最终会将客户端的请求转换为对单个字段的计算,例如Name字段,可能会从Oralce获取。 Age从Mysql中获取。而Address则是根据referer的IP地址计算。 虽然使用GraphQL的大部分场合中,字段都是和DB有着很紧密的联系,但每个字段的处理也可以脱离DB而单独计算。

总结

最后,为GraphQL来一个总结。

Restful API为所有的资源都创建了相对应的endpoint。一个Endpoint代表一中特定的资源类型,客户端需要多种资源时,需要调用多次Endpoint才可以得到预期结果,客户端对数据没有控制权,也就是说服务端定义成什么样,客户端获取的就是什么样,客户端没有say No的权力。 而GraphQL恰恰相反,GraphQL将所有资源都汇聚成了一个Endpoint,GraphQL允许客户端自由操纵数据,而服务端仅仅充当翻译器和网关的作用。

GraphQL将数据操作权交换给了客户端,允许客户端对数据进行裁剪。这或许是GraphQL与Restful API最根本的区别吧。