【译】GraphQL 初学者指南

3,496 阅读10分钟

API 是互联网行业讨论最多的术语之一,但是很多人并不确切知道 API 到底是什么。基本上,API 代表应用程序编程接口(Application Programming Interface)。顾名思义,它相当于一个“界面”,使人们(开发人员、用户、消费者)能够通过它与数据进行交互。

你可以理解为 API 就像是个酒保,你向酒保要一杯酒,他便把一杯你想要的酒递给你,看起来很简单的事,还有什么问题呢?

构建 API 不是一件困难的事,但学习和理解具体的 API 可能存在一定的困难。多数开发者都会使用你的 API 去开发功能或者只是消费数据,这就要求 API 需要尽可能保持干净、直观。精心设计、直观性强的 API 会非常容易理解和使用。

一直以来,我们都在用 REST 来构建 API,使用这种方式会存在一些问题,例如:

  • 多端点
  • 开发人员学习理解 API 的成本比较高
  • API 的信息过度和信息不足问题

为了解决以上的类似问题,Facebook 创造了 GraphQL。我认为 GraphQL 是当代构建 API 的最佳方式,本文将阐述为什么你应该开始学习 GraphQL,让你了解 GraphQL 的工作原理,以及如何使用 GraphQL 创建设计精良、高效并且功能强大的 API。

你可能已经听说过 GraphQL,因为很多开发者和公司都在使用它。由于 GraphQL 是开源的,所以社区也是很庞大的存在。让我们在实践中学习 GraphQL 的工作原理,领略它的魔力了。

什么是GraphQL?

GraphQL (译者注:中文文档)是 Facebook 开发的一种开源查询语言。它为我们提供了一种更有效的方式来设计、创建和消费我们的 API 。基本上,它是 REST 的替代品。

GraphQL 有很多功能,例如:

  1. 我们可以编写自己所需的数据,并获得所需的数据。不用再像我们习惯使用的 REST 那样过度获取信息
  2. GraphQL 为我们提供了单个端点,不再为同一 API 提供版本2或版本3。
  3. GraphQL 是强类型的,所以我们可以在执行之前在 GraphQL 类型系统中验证查询。有助于构建更强大的 API。

这是对 GraphQL 的基本介绍,解释了为什么它如此强大以及为什么它如今获得了很多人气。如果想了解更多相关信息,我建议访问 GraphQL 网站去了解。

入门

本文主要目的不是学习如何配置 GraphQL 服务器,所以我们现在不会深入研究。本文的目标是了解 GraphQL 在实践中的工作原理,因此我们将使用一个叫做☄️Traderpack的零配置GraphQL服务器库。

第一步,我们需要创建一个新文件夹,你可以随意命名。我将它命名为 graphql-server :

mkdir graphql-server

假定你已经在自己的机器中安装了 npm 或 yarn。如果你不知道它们是什么,npm 和 yarn 是 JavaScript 编程语言的包管理器。对于 Node.js,默认包管理器是 npm。

在创建的文件夹中,输入以下命令:

npm init -y

如果你使用 yarn :

yarn init 

npm 会创建一个 package.json 文件,项目安装的所有依赖项和命令都在这个文件中。

接下来,我们来安装本项目要使用的唯一依赖项,☄️Traderpack 允许你创建零配置的 GraphQL 服务器。由于我们刚刚开始使用 GraphQL,这将帮助我们继续学习更多内容,而不必担心服务器配置。

打开终端进入项目文件夹,键入以下命令:

npm install --save-dev graphpack

如果你使用 yarn,应该这样:

yarn add --dev graphpack

安装 Graphpack 之后,转到 package.json 文件中的脚本,并在其中输入以下代码:

"scripts": {
    "dev": "graphpack",
    "build": "graphpack build"
}

我们将创建一个名为 src 的文件夹作为整个服务器中唯一的文件夹,然后在 src 文件夹中创建三个文件。

译者注:使用 VSCode 的童鞋可以安装 `GraphQL for VSCode` 插件进行语法高亮

首先,我们创建第一个文件,名叫 schema.graphql,并在这个文件中输入以下代码:

type Query {
  hello: String
}

这个 schema.graphql 文件将是我们的整个 GraphQL 架构,稍后我会解释它的作用。

接下来,在 src 文件夹中创建第二个文件 resolvers.js,并在这个文件中输入以下代码:

import { users } from "./db"

const resolvers = {
  Query: {
    hello: () => "Hello World!"
  }
}

export default resolvers

这个 resolvers.js 文件将是我们提供将 GraphQL 操作转换为数据的指令的方式。

最后,在src文件夹中创建第三个文件 db.js,并在这个文件中输入以下代码:

export let users = [
  { id: 1, name: "John Doe", email: "john@gmail.com", age: 22 },
  { id: 2, name: "Jane Doe", email: "jane@gmail.com", age: 23 }
]

在本教程中,我们没有使用真实数据库。所以这个 db.js 文件将模拟数据库,仅用于学习目的。

现在我们的src文件夹应如下所示:

src
  |--db.js
  |--resolvers.js
  |--schema.graphql

现在,如果你运行命令 npm run dev,或者如果你正在使用yarn,则运行 yarn dev,应该在终端中看到此输出:

现在可以用浏览器打开 localhost:4000,这意味着我们已准备好开始在 GraphQL 中编写我们的第一个 queries(查询),mutations(突变)和 subscriptions(订阅)。

如果感兴趣可以看看 GraphQL Playground,这是一个功能强大的 GraphQL IDE,可用于更好的开发工作流程。如果你想了解有关 GraphQL Playground 的更多信息,请单击此处

Schema

GraphQL 有自己的语言类型,用于编写 Schema(模式)。这是一种人类可读的模式语法,称为 Schema Definition Language - 模式定义语言(SDL)。无论使用何种技术,SDL都是相同的 - 你可以将其用于你想要的任何语言或框架。这种模式语言非常有用,因为它会让你很容易直观的理解你的 API 将具有哪些类型。

Types

Types(类型)是 GraphQL 最重要的特性之一。Types 是自定义对象,表示 API 的外观。举个例子,如果你正在构建社交媒体应用程序,那么你的 API 可能会有 PostsUsersLikesGroups 等 Types。

Types 下有 fields(字段),这些字段返回特定类型的数据。例如,我们要创建一个 User 类型,我们应该有 nameemailage 等字段。字段可以是任何类型,并始终返回一种数据类型,比如 Int、Float、String、Boolean、ID、对象类型列表或自定义对象类型。

现在编写我们的第一个 Type,转到 schema.graphql 文件并用以下内容替换已存在的 Query 类型:

type User {
  id: ID!
  name: String!
  email: String!
  age: Int
}

每个 User 都将拥有一个 ID,因此我们为其提供了 ID 类型。User 也会有一个 nameemail,所以我们给它们 String 类型,年龄我们给了 Int 类型。很简单吧?

数据类型后面的 ! 表示该字段非空(non-nullable),这意味着这些带 ! 的字段必须在每个查询中返回一些数据。

在 GraphQL 中有三个主要概念:

  1. 查询(queries) - 从服务器获取数据
  2. 突变(mutations) - 修改服务器上的数据并获取更新数据(创建,更新,删除)
  3. 订阅(subscriptions) - 与服务器保持实时连接

这三个主要概念我会一一解释。

Queries

简单来说,GraphQL 中的查询就是获取数据的方式。GraphQL 中的查询会获得所需的确切数据。不多也不少。这对我们的 API 产生了巨大的积极影响 - 不再像我们使用 REST API 那样过度获取或提取不足信息。

让我们来中创建一个查询类型,首先,在 schema.graphql中添加一个名为 Query 的新类型:

type Query {
  users: [User!]!
}

users 查询将返回给我们一个或多个用户的数组。它不会返回 null,因为我们在后面加了 ! ,这意味着它是一个不可为空的查询,应该总是返回一些东西。

既然能返回多个,我们也能返回一个特定用户,在 Query 类型中添加一个新的查询 user:

  type Query {
    users: [User!]!
+   user(id: ID!): User!
  }

你会发现查询能够传递参数,查询特定用户的时候把用户的 id 作为参数传入。

那么 GraphQL 如何知道去哪获取到数据返回呢?这就是 resolvers.js 的作用,它会告诉 GraphQL 如何以及在何处获取数据。

打开 resolvers.js 文件,更改一下代码:

import { users } from "./db"

const resolvers = {
  Query: {
    user: (parent, { id }, context, info) => {
      return users.find(user => user.id === id)
    },
    users: (parent, args, context, info) => {
      return users
    }
  }
}

export default resolvers

解释一下以上代码是如何工作的:

  • 每个查询解析器都有四个参数。在 user 函数中,我们将 id 作为参数传递,然后返回与传递的 id 匹配的特定用户
  • users 函数中,我们只是返回已存在的 users 数组,它会返回所有的用户

现在,我们将测试我们的查询是否正常工作。浏览器打开 localhost:4000,在左边输入以下代码然后点运行按钮:

query {
  users {
    id
    name
    email
    age
  }
}

试试返回 id 为 1 的用户:

query {
  user(id: 1) {
    id
    name
    email
    age
  }
}

Mutations

在 GraphQL 中,Mutations 是修改服务器上的数据并获取更新数据的方式。你可以理解为类似 REST 的CUD(CREATE,UPDATE,DELETE)。

我们来创建一个类型突变,我们所有的突变都将在这种类型中结束。在 schema.graphql 文件中编写一个名为 mutation 的新类型:

type Mutation {
  createUser(id: ID!, name: String!, email: String!, age: Int): User!
  updateUser(id: ID!, name: String, email: String, age: Int): User!
  deleteUser(id: ID!): User!
}

上述代码创建了三个 Mutation:

  1. createUser - 我们传入的参数 idnameemailage,应该返回一个新的用户
  2. updateUser - 传入 id 和新的 nameemailage, 应该返回一个更新后的用户
  3. deleteUser - 传入 id ,应该返回被删除掉用户的信息

现在,我们去 resolvers.js 文件中,在 Query 对象下方插入一个新的 Mutation 对象。

Mutation: {
    createUser: (parent, { id, name, email, age }, context, info) => {
      const newUser = { id, name, email, age }
      users.push(newUser)
      return newUser
    },
    updateUser: (parent, { id, name, email, age }, context, info) => {
      let newUser = users.find(user => user.id === id)
      newUser.name = name
      newUser.email = email
      newUser.age = age
      return newUser
    },
    deleteUser: (parent, { id }, context, info) => {
      const userIndex = users.findIndex(user => user.id === id)
      if (userIndex === -1) throw new Error("User not found.")
      const deletedUsers = users.splice(userIndex, 1)
      return deletedUsers[0]
    }
  }

该去 localhost:4000 看看我们写的 Mutations 是否有效了,在页面上输入:

mutation {
  createUser(id: 3, name: "Robert", email: "robert@gmail.com", age: 21) {
    id
    name
    email
    age
  }
}

你也可以尝试一下其他的 Mutation。

Subscriptions

正如我之前所说,Subscriptions (订阅)是你与服务器保持实时连接的方式。这意味着无论何时在服务器中发生事件,并且每当调用该事件时,服务器都会将相应的数据发送到客户端。通过使用订阅,你可以将应用程序更新为不同用户之间的最新更改。

最基本的订阅示例:

subscription {
  users {
    id
    name
    email
    age
  }
}

你会说它与查询非常相似,但它的工作方式和查询不同。当服务器中的某些内容更新时,服务器将运行订阅中指定的 GraphQL 查询,并将更新的结果发送到客户端。我并不打算在这篇文章中使用订阅,但如果你想了解更多关于它们的信息,请点击此处

总结

如你所见,GraphQL 是一项非常强大的新技术。它为我们提供了构建更好和精心设计的 API 的真正能力。这就是为什么我建议你现在开始学习它。

对我来说,它最终将取代 REST。

感谢阅读文章,请在下面发表评论!