当谈论到客户端与服务端之间的网络请求时,REST 绝对是连接两者的方案中最流行的选择。在 REST 中,所有概念都是可以通过 URL 可访问的资源演化而来的。你可以通过一个 HTTP GET 请求读取一个资源,通过一个 HTTP POST 请求创建一个资源,或者通过 HTTP PUT 和 HTTP DELETE 更新或删除一个资源。这些被称为 CRUD(Create、Read、Update、Delete)操作。资源可以是任何实体,比如作者(authors)、文章(articles)或者用户(users)。使用 REST 时,传输数据的格式并没有定论,但是多数人会使用 JSON 作为传输数据媒介。最终,REST 使应用之间能直接通过原生的 HTTP URL 以及 HTTP 方法进行数据交互。
// 一个 RESTful 请求
GET https://api.domain.com/authors/7
// JSON 数据的响应
{
"id": "7",
"name": "Robin Wieruch",
"avatarUrl": "https://domain.com/authors/7",
"firstName": "Robin",
"lastName": "Wieruch"
}
我们先回答下这个问题:什么是 GraphQL?GraphQL是由 Facebook 在 2012 年创立的一门开源查询语言。在它开源之前,Facebook 就已经在内部移动端应用中使用过。为什么选用移动应用?GraphQL 作为通用的 REST 架构的替代方案而被开发出来,它允许客户端只请求其需要的数据——不多也不少,一切在客户端的主导下。在一个 RESTful 架构下,因为后端开发人员定义在各个 URL 的资源上返回的数据,而不是前端开发人员来提出数据需求,使得按需获取数据会非常困难。经常前端需要请求一个资源中所有的信息,即便只需要其中的一部分数据。这个问题被称之为过度获取(overfetching)。最恶劣的场景下,一个客户端应用不得不请求多个而不是一个资源,这通常会发起多个网络请求。这不仅会造成过度获取的问题,也会造成瀑布式的网络请求(waterfall network requests)。那么将像 GraphQL 之类的查询语言,不仅在服务端程使用,也应用到客户端的话,客户端来决定需要什么数据,这样只需要发送一个请求到服务端。在 Facebook 的 GraphQL 移动端开发场景下,这极大地减少了忘了请求,因为 GraphQL 一次只需要发起一个请求,并且传输中数据数量也减少了。
Facebook 开源了 GraphQL 标准和其 JavaScript 版本的实现。后来主要编程语言也实现了标准。此外,GraphQL 周边的生态不仅仅水平上扩展了不同语言的实现,并且还出现了在 GraphQL 基础上实现了类库(比如 Apollo 和 Relay)。
一个 GraphQL 操作可以是一个查询(query(读操作))、修改(mutation(写操作))以及订阅(subscription(持续读操作))。这些操作中每一种都只是根据 GraphQL 标准构造的一段字符串而已。一旦一个 GraphQL 操作从前端应用到达后端应用,首先会在后端解释整个 GraphQL schema,然后再为前端解析相关的数据。GraphQL 并没有要求网络层选型(通常是 HTTP),也没有要求传输数据格式(通常是 JSON)。甚至没有要求应用架构(通常是前后端分离架构)。它只是一个查询语言。
// GraphQL 查询
author(id: "7") {
id
name
avatarUrl
articles(limit: 2) {
name
urlSlug
}
}
// GraphQL 查询结果
{
"data": {
"author": {
"id": "7",
"name": "Robin Wieruch",
"avatarUrl": "https://domain.com/authors/7",
"articles": [
{
"name": "The Road to learn React",
"urlSlug": "the-road-to-learn-react"
},
{
"name": "React Testing Tutorial",
"urlSlug": "react-testing-tutorial"
}
]
}
}
}
如你所见,一个查询请求了多个资源(作者(author)、文章(article)),在 GraphQL 中被称为字段(fileds),及时 GraphQL schema提供了更多是数据(比如文章中的描述(description)和发布时间(releaseDate) ),我们只会拿到这些字段的一个子集(文章中的名称(name)和 urlSlug)。相对地,在 RESTful 架构中,需要至少两个连续请求分别获取作者实体和它的文章,GraphQL 只需要一个查询就可以做到。此外,查询只需要选择必要的字段即可,而不是整个数据实体。
优点:
缺点:
更多详细内容可以看这里:https://www.jianshu.com/p/f45fe96de908
利用两分钟时间,我们用prisma-server+MemFireDB+docker手把手一步一步进行一下简单实战。
prisma和MemFireDB访问地址见最后引用章节
这里我们先准备一个ubuntu18.4,安装docker并关闭防火墙(很重要)
apt-get update
apt-get install docker
sudo ufw disable
sudo iptables -F
安装最新版本的nodejs
sudo npm config set registry https://registry.npm.taobao.org
sudo npm install n -g
sudo n stable
安装prisma的CLI工具,这里安装的是prisma1,最新prisma2已经不提供graphql-server镜像了
npm install -g prisma1
编辑docker-compose文件,主要要修改数据库服务地址、数据库名、用户名和密码,这里直接在MemFireDB控制台上查看获取
mkdir hello-world
cd hello-world
touch docker-compose.yml
cat docker-compose.yml
version: '3'
services:
prisma:
image: prismagraphql/prisma:1.34
restart: always
ports:
- '4466:4466'
environment:
PRISMA_CONFIG: |
port: 4466
databases:
default:
connector: postgres
host: ""
port: ""
user: ""
password: ""
database: ""
volumes:
postgres: ~
启动prisma-server
docker-compose up -d
查看prisma-server是否启动成功,数据库是否连接正常
docker ps # 查看容器id
docker logs {容器id}
下面的日志代表服务已经正常启动
[INFO] Obtaining exclusive agent lock...
[INFO] Initializing workers...
[INFO] Successfully started 1 workers.
Server running on :4466
[INFO] Obtaining exclusive agent lock... Successful.
[INFO] Deployment worker initialization complete.
可以在浏览器上面直接访问
为之前下载的cli配置server的地址信息
prisma1 init --endpoint http://localhost:4466
向prisma-server中部署api,这里会根据datamode生成api接口,提供给其他客户端应用来访问
prisma1 deploy
在prisma-server的后端可以进行数据管理
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-95E53jLf-1626142483410)(https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/2251894b5f884304a6aa0862ca06543f~tplv-k3u1fbpfcp-watermark.image)]
使用dbeaver连接MemFireDB,我们也可以看到刚刚创建好的schema和table
我们可以插入一些数据
再次通过GraphQL查看所有数据
如果接口有变动,我们无需修改任何后端代码,只需修改前端查询语句即可,再次查询指定id的User信息
因为 REST 提出通过 URL 来标识资源,最终常常会出现低效的连续请求。比方说,最开始你通过 id 来定位一个作者实体,然后你通过作者的 id 获取的某个信息来请求他所有的文章。在 GraphQL 中只需要一个请求就能办到,这是更加效率的。更进一步而言,如果你只想获取作者的所有文章数据,而不关心作者的信息,GraphQL 允许只你选择你需要的信息。在 REST 中,你需要先获取作者的所有实体信息,即使你只关心这个作者的文章而已。
最终 GraphQL 将由服务端主导返回什么数据变成了客户端决定需要什么。