项目仓库:
译者: Seymoe https://github.com/seymoe
译者注: 此文为本人第一篇译文, 本人作为无 GraphQL 使用背景的初学者, 翻译过程亦是学习过程. 如果发现译文有错误或者表述不当的地方敬请指正, 十分感谢!
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 https://graphql.org/ (译者注: 中文文档 http://graphql.cn/ )是 Facebook 开发的一种开源查询语言. 它为我们提供了一种更有效的方式来设计, 创建和消费我们的 API . 基本上, 它是 REST 的替代品.
GraphQL 有很多功能, 例如:
我们可以编写自己所需的数据, 并获得所需的数据. 不用再像我们习惯使用的 REST 那样过度获取信息.
GraphQL 为我们提供了单个端点, 不再为同一 API 提供版本 2 或版本 3.
GraphQL 是强类型的, 所以我们可以在执行之前在 GraphQL 类型系统中验证查询. 有助于构建更强大的 API.
这是对 GraphQL 的基本介绍, 解释了为什么它如此强大以及为什么它如今获得了很多人气. 如果想了解更多相关信息, 我建议访问 GraphQL https://graphql.org/ 网站去了解.
入门
本文主要目的不是学习如何配置 GraphQL 服务器, 所以我们现在不会深入研究. 本文的目标是了解 GraphQL 在实践中的工作原理, 因此我们将使用一个叫做Traderpack https://github.com/glennreyes/graphpack 的零配置 GraphQL 服务器库.
第一步, 我们需要创建一个新文件夹, 你可以随意命名. 我将它命名为 graphql-server :
mkdir graphql-server
假定你已经在自己的机器中安装了 NPM 或 yarn. 如果你不知道它们是什么, NPM 和 yarn 是 JavaScript 编程语言的包管理器. 对于 Node.JS, 默认包管理器是 NPM.
在创建的文件夹中, 输入以下命令:
NPM init -y
如果你使用 yarn :
yarn init
NPM 会创建一个 package.JSON 文件, 项目安装的所有依赖项和命令都在这个文件中.
接下来, 我们来安装本项目要使用的唯一依赖项,Traderpack https://github.com/glennreyes/graphpack 允许你创建零配置的 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 可能会有 Posts,Users,Likes,Groups 等 Types.
Types 下有 fields(字段), 这些字段返回特定类型的数据. 例如, 我们要创建一个 User 类型, 我们应该有 name,email 和 age 等字段. 字段可以是任何类型, 并始终返回一种数据类型, 比如 Int,Float,String,Boolean,ID, 对象类型列表或自定义对象类型.
现在编写我们的第一个 Type, 转到 schema.graphql 文件并用以下内容替换已存在的 Query 类型:
- type User {
- id: ID!
- name: String!
- email: String!
- age: Int
- }
每个 User 都将拥有一个 ID, 因此我们为其提供了 ID 类型. User 也会有一个 name 和 email, 所以我们给它们 String 类型, 年龄我们给了 Int 类型. 很简单吧?
数据类型后面的 ! 表示该字段非空(non-nullable), 这意味着这些带 ! 的字段必须在每个查询中返回一些数据.
在 GraphQL 中有三个主要概念:
查询(queries) - 从服务器获取数据
突变(mutations) - 修改服务器上的数据并获取更新数据(创建, 更新, 删除)
订阅(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
- age
- }
- }
试试返回 id 为 1 的用户:
- query {
- user(id: 1) {
- id
- name
- 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:
createUser - 我们传入的参数 id,name,email,age, 应该返回一个新的用户
updateUser - 传入 id 和新的 name,email 与 age, 应该返回一个更新后的用户
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
- age
- }
- }
你也可以尝试一下其他的 Mutation.
Subscriptions
正如我之前所说, Subscriptions (订阅)是你与服务器保持实时连接的方式. 这意味着无论何时在服务器中发生事件, 并且每当调用该事件时, 服务器都会将相应的数据发送到客户端. 通过使用订阅, 你可以将应用程序更新为不同用户之间的最新更改.
最基本的订阅示例:
- subscription {
- users {
- id
- name
- age
- }
- }
你会说它与查询非常相似, 但它的工作方式和查询不同. 当服务器中的某些内容更新时, 服务器将运行订阅中指定的 GraphQL 查询, 并将更新的结果发送到客户端. 我并不打算在这篇文章中使用订阅, 但如果你想了解更多关于它们的信息, 请点击此处.
总结
如你所见, GraphQL 是一项非常强大的新技术. 它为我们提供了构建更好和精心设计的 API 的真正能力. 这就是为什么我建议你现在开始学习它.
对我来说, 它最终将取代 REST.
来源: https://juejin.im/post/5c32119651882525a50bb436