手把手带你领略graphql的魅力

641 阅读6分钟

背景介绍

当谈论到客户端与服务端之间的网络请求时,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?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 只需要一个查询就可以做到。此外,查询只需要选择必要的字段即可,而不是整个数据实体。

GraphQL的优缺点

优点:

  • 声明式地数据获取
  • 在 GraphQL 中没有过度获取
  • 在 React、Angular、Node 和 Co 中的 GraphQL
  • GraphQL 自省(Introspection)
  • ……

缺点:

  • GraphQL 查询的复杂性
  • 查询频率限制
  • GraphQL 缓存

更多详细内容可以看这里:www.jianshu.com/p/f45fe96de…

实战

利用两分钟时间,我们用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.

可以在浏览器上面直接访问

image.png

为之前下载的cli配置server的地址信息

prisma1 init --endpoint http://localhost:4466

向prisma-server中部署api,这里会根据datamode生成api接口,提供给其他客户端应用来访问

prisma1 deploy

在prisma-server的后端可以进行数据管理

image.png

使用dbeaver连接MemFireDB,我们也可以看到刚刚创建好的schema和table

image.png

我们可以插入一些数据

image.png

再次通过GraphQL查看所有数据

image.png

如果接口有变动,我们无需修改任何后端代码,只需修改前端查询语句即可,再次查询指定id的User信息

image.png

总结

因为 REST 提出通过 URL 来标识资源,最终常常会出现低效的连续请求。比方说,最开始你通过 id 来定位一个作者实体,然后你通过作者的 id 获取的某个信息来请求他所有的文章。在 GraphQL 中只需要一个请求就能办到,这是更加效率的。更进一步而言,如果你只想获取作者的所有文章数据,而不关心作者的信息,GraphQL 允许只你选择你需要的信息。在 REST 中,你需要先获取作者的所有实体信息,即使你只关心这个作者的文章而已。

最终 GraphQL 将由服务端主导返回什么数据变成了客户端决定需要什么。

引用