GraphQL 入门
最近刚刚学习了 GraphQL,趁着新鲜,总结一下 GraphQL 的概念与使用。主要针对没有听说过,或者听说过但没有看过用过这个技术的同学们,给大家一点参考。
# 什么是 GraphQL
GraphQL is a query language for your API, and a server-side runtime for executing queries by using a type system you define for your data. GraphQL isn't tied to any specific database or storage engine and is instead backed by your existing code and data.
GraphQL是一种针对 API 的语言。它运行在服务器端,不局限与某一种数据库或数据存储机制。它是对你已有数据处理方式的一种更好的支持。
为什么要用 GraphQL GraphQL 常常拿来与 REST API 进行对比,它与 REST API 返回数据的策略不同。
REST API 更多强调的是把所有数据看作一种资源,一个 API Endpoint 返回一种数据。
假设我们有以下两个表:
User {
id
name
age
}
Post {
id
title
content
author_id
}
2
3
4
5
6
7
8
9
10
11
12
GET /user/{id}
会返回一个用户,GET /post/{id}
会返回一篇文章。如果我们想表示某篇文章的作者,则会在 post
里返回一个 author_id
作为外键。如果我们想知道某一篇文章的作者是谁,则在拿到 GET /post/{id}
的返回值后需要再调用 GET /user/{id}
获取该用户的信息。这样就需要向服务器发送两个请求。
GET /posts
会返回一个文章列表,每一篇文章会有一个 author_id
。假设返回 10 (N) 篇文章,如果我们想显示每篇文章作者的姓名,则需要再向服务器发送 10 (N) 个 GET /user/{id}
来获取每个人的名字。这样就总共需要发送 11 (N + 1)个请求。
而且,每个 GET /user/{id}
都会在返回作者姓名的同时返回作者的年龄,而这是我们并不需要的。这时候你也许会说,返回值中有我们不需要的信息也没什么关系,只要我们需要的信息包含其中就可以了。从逻辑上来说是没错,但是想像一下如果实际应用当中用户属性很多,而每次都返回用户全部信息的话,则会极大的占用带宽资源。本来需要的信息可能只有1KB,而API返回了100KB 的信息,尤其是现在的应用大多都需要支持移动端,需要下载的数据量大小直接影响了你的应用的响应速度,进而影响了用户体验。
这时候我们就想,如果可以只发送一个 API 请求,就能拿到我们想要的所有数据,并且只返回我们需要的信息,那就会在一定程度上缩短系统的响应时间,提升用户体验。而 GraphQL 就在这时应运而生了。
# GraphQL 举例
用 GraphQL 之后,当我们需要一个文章列表以及每篇文章的作者的时候,我们只需要发送一个 API 请求 POST /graphql
,该请求的 body为:
{
posts {
id
title
author {
id
name
}
}
}
2
3
4
5
6
7
8
9
10
其中 author
对应的就是 User
表。该 API 就会返回
{
posts: [
{
id: 1,
title: "文章1",
author: {
id: 1,
name: "张三"
}
},
{
id: 2,
title: "文章2",
author: {
id: 2,
name: "李四"
}
}
]
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
可以看出,与 REST API 设计思路不同,GraphQL 只需要一个API 接口,而返回的是什么数据取决于请求的 body 传的参数,并且返回值包含哪些信息也直接定义在参数中,没有请求的数据则不会返回。
# GraphQL 实现原理
GraphQL 的核心在于一个你自己定义的 schema 文件,比如:
type User {
id: ID
name: String
age: String
posts: [Post]
}
type Post {
id: ID
title: String
content: String
author: User
}
2
3
4
5
6
7
8
9
10
11
12
13
从这个 schema 文件中我们可以看到,Post 里的 author
就把 Post 和 User 联系在了一起,而与此同时,User 里的 posts 也可以让我们在拿用户信息的时候同时得到这个用户写过的文章。
而至于具体 GraphQL 是如何把相关的信息整合在一起的,则取决于你已经有的数据表达形式。如果你没有已经做好的 API,则可以直接从数据库中提取数据。如果你有已经做好的 API,则可以直接从这些 API 中提取出需要的信息。或者部分信息由已做好的 API 提供,部分信息直接从数据库中提取。
实际上,如果你已经做好了完整的 REST API(比如 GET /user/{id}
,GET /users
,GET /post/{id}
,GET /posts
),而只想把 GraphQL 包在你的 API 外边(即 GraphQL 完全只从 API 中提取数据)的话,在以上的例子中,GraphQL 还是会向你的 API 服务器发送 N+1 个请求以获取需要的数据。但是与直接发送请求给 API 的区别在于,你的前端应用只是向 GraphQL 发送了一个请求。 GraphQL 服务器与 API 服务器之间的调用往往比前端调用 API 快的多,如果你的 GraphQL 与 API 是部署在同一台机器上的话,那响应时间则会更快。所以总体来说即使在这种情况下,用 GraphQL 的响应时间还是比直接用 API 要短。
# 版本管理
使用 REST API 时候的另一个问题就是版本管理问题。比如:刚开始设计的时候 Post 的标题是用 name
来表示的,后来要改成 title
。这时候考虑到以前的前端应用都是用的 name
,为了兼容之前的用法,就需要把这个改动变成一个新的 API 版本,即 GET /v1/post
使用的是 name
, GET /v2/post
使用的是 title
。而对于 GraphQL 则不存在这个问题,只需要把 schema
改成
type Post {
id: ID
name: String
title: String
content: String
author: User
}
2
3
4
5
6
7
就可以同时兼容 title
和 name
的使用
# 总结
GraphQL 是一种在 API 的基础上,更好的整合数据的方法。其优点如下:
- 前端应用只需发送一个请求即可得到所有需要的信息
- 返回信息中只包含用户请求的信息,没有多余信息返回
- 可以直接使用已有的 API 来提供数据,不需要重新设计和重构代码
- 不需要版本设计,可以轻松支持数据名称、类型、结构的改动 GraphQL 的概念及使用先到这里,之后会专门写一篇 GraphQL 的具体应用方法。