转自:https://segmentfault.com/a/1190000017851838
什么是 GraphQL
GraphQL 是 Facebook 开发的一种开源查询语言。它为我们提供了一种更有效的设计、创建和使用 Api的方法。从根本上说,它是 REST 的替代品。
GraphQL有很多特性,比如:
- GraphQL查询总是能准确获得你想要的数据,不多不少,所以返回的结果是可预测的, 不再像你使用 REST 那样过度获取信息。
- 它为我们提供了同一个端,对于同一个 API,没有版本2或版本3。给 GraphQL API 添加字段和类型而无需影响现有查询,老旧字段可以废弃,从工具中隐藏。
- GraphQL是强类型的,通过它,可以在执行之前验证 GraphQL 类型系统中的查询, 它帮助我们构建更强大的 Api。
这是对 GraphQL 的基本介绍——为什么它这么强大,为什么它现在这么流行。如果你想了解更多关于它的信息,可以访问 GraphQL网站 学习。
开始
本文的主要目的不是学习如何设置 GraphQL服务器,所以我们现在还没有深入研究。 文本的目标是了解 GraphQL 在实践中的工作原理,因此这里使用简约的零配置 GraphQL 服务器的 Graphpack。
开始项目前,首先我们创建一个新文件名为 graphql-server, 在 mac 终端执行
mkdir graphql-server
接着进入该文件,执行
npm init -y
或者执行
yarn init
npm 将创建一个包 package.json
文件,这个包是你安装的所有依赖项和命令。
现在,我们要安装唯一的依赖项。
Graphpack 允许创建零配置的 GraphQL 服务器。由于刚刚开始使用 GraphQL,这将帮助我们继续学习GraphQL 更多的内容,而不必担心服务器配置,执行以下命令:
npm install --save-dev graphpack
或者使用 yarn 安装:
yarn add --dev graphpack
安装 Graphpack 之后,转到 package.json文件中的脚本,并在其中输入以下代码:
"scripts": {
"dev": "graphpack",
"build": "graphpack build"
}
接着创建一个名为 src 的文件夹,它将是整个服务器中唯一的文件夹。
在 src 文件夹中创建一个名为 schema.graphql 的文件,并写入以下代码:
type Query {
hello: String
}
在 schema.graphql 文件将是我们的整个 GraphQL的模式(Schema)。
接着在 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 dev
启动服务
在浏览器打开 localhost:4000
。这里就实现我们在 GraphQL 中的第一个查询,更改和订阅,打开界面如下:
你可以看到 GraphQL Playground,这是一个功能强大的 GraphQL IDE,可用于更好的开发工作流程。 如果你想了解有关 GraphQL Playground的更多信息,请单击此处。
模式(Schema)
GraphQL 有自己的语言类型,用于编写模式。 这是一种人类可读的模式语法,称为规范与描述语言(SDL)。无论使用何种技术,SDL 都是相同的 - 你可以将其用于你想要的任何语言或框架。
这种模式语言非常有用,因为它更直观的看出 API 具有哪些类型,一看到 API 就知道怎么使用。
类型(Type)
类型是 GraphQL 最重要的特性之一。类型是表示 API 外观的自定义对象。例如,如果你正在构建一个社交媒体应用程序,那么你的 API 应该具有诸如文章、用户、赞、组等类型。
类型具有字段,这些字段返回特定类型的数据。 例如,我们要创建一个 User
类型,我们应该有一些 name
,email
和 age
字段。 类型字段可以是任何类型,并始终返回一种数据类型,如 Int,Float,String,Boolean,ID,对象类型列表或自定义对象类型。
现在编写的第一个 Type,在 schema.graphql 文件中用以下内容替换已存在的 Query 类型:
type User {
id: ID!
name: String!
email: String!
age: Int
}
每个用户都将拥有一个 ID,因此为其提供了 ID 类型。 用户也会有一个 name
和 email
,所以给它一个字符串类型和一个 Int 类型。
但是,在每一行的结尾的 !
呢? 感叹号表示字段不可为空,这意味着每个字段必须在每个查询中返回一些数据。 User 中唯一可以为空的字段是 age
。
在GraphQL中,有三个主要概念:
- query (查询) — 从服务器获取数据的方式。
- mutation (更改) — 修改服务器上的数据并获取更新数据的方法(创建、更新、删除)。
- subscription (订阅) — 当希望数据更改时,可以进行消息推送,使用 subscription 类型(针对当前的日趋流行的 real-time 应用提出的)。
query (查询)
为了简单地解释这一点,GraphQL 中的查询是获取数据的方式。关于 GraphQL 中的查询,最吸引人的地方之一就是你将获得所需的确切数据,不多不少。这对我们的 API 产生了巨大的积极影响——不再像 REST API 那样获取过多或不足的信息。
我们将在 GraphQL 中创建第一个类型的 Query。 我们所有的查询都将以此类型结束。 首先,在文件 schema.graphql 编写一个名为Query 的新类型:
type Query {
users: [User!]!
}
这很简单:用户查询将返回给我们一个或多个用户的数组。 它不会返回 null
,因为我们放入了 !
,这意味着它是一个不可为空的查询, 它总会返回一些数据。
但我们也可以返回特定用户。 为此,创建一个名为 user
的新查询。 在我们的 Query 类型中,写以下代码:
user(id: ID!): User!
现在 Query 类型应该是这样的:
type Query {
users: [User!]!
user(id: ID!): User!
}
如上所见,使用 GraphQL 中的查询,还可以传递参数。在本例中,要查询特定用户,所以要传递其用户的 ID。
但是,你可能想知道: GraphQL 如何知道从哪里获取数据? 这就是为什么我们应该有一个 resolvers.js 文件。该文件告诉 GraphQL 它将如何以及在何处获取数据。
首先,看看我们的 resolvers.js 文件并里该文件里导入 db.js
文件。我们刚才创建的 resolvers.js 文件内容如下:
import { users } from "./db";
const resolvers = {
Query: {
hello: () => "Hello World!"
}
};
export default resolvers;
现在,我们将创建第一个 Query,在 resolvers.js 文件并替换 hello
函数。 现在 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
匹配的特定 user,这很简单。
在 users
函数中,我们只是返回已存在的 users 数组,这个数组存放的是所有的用户。
现在,我们将测试查询是否工作正常,转到 localhost:4000,输入以下代码:
query {
users {
id
name
email
age
}
}
它应该返回给你我们所有的用户。
如果想返回特定的用户:
query {
user(id: 1) {
id
name
email
age
}
}
mutation (更改)
在 GraphQL 中,更改是修改服务器上的数据并获取更新数据的方式, 你可以像 REST 的CUD(创建,更新,删除)一样思考。
在 GraphQL 中创建我们的第一个类型修改,这里所有的修改都将在这个类型中结束。 首先,在 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!
}
这里主要定义三个修改数据的方法:
- 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];
}
}
现在 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;
}
},
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];
}
}
};
export default resolvers;
现在,我们要测试我们的 mutations 是否正常。转到localhost:4000,输入以下代码:
mutation {
createUser(id: 3, name: "Robert", email: "robert@gmail.com", age: 21) {
id
name
email
age
}
}
subscription (订阅)
如我之前所说,订阅是你与服务器保持实时连接的方式。这意味着无论何时在服务器中发生事件,并且每当调用该事件时,服务器都会将相应的数据发送到客户端。
通过订阅,你可以让你的应用在不同的用户之间保持更新。
基本订阅是这样的:(sample.graphql )
subscription {
users {
id
name
email
age
}
}
你会说它非常类似于查询,是的, 但它的工作方式不同。
当服务器中发生更新时,服务器将运行订阅中指定的 GraphQL 查询,并向客户机发送一个新更新的结果。
在这篇文章中,我们不打算讨论订阅,但是如果你想阅读更多关于订阅的信息,请单击这里。
总结
如你所见,GraphQL 是一项非常强大的新技术。 它为我们提供了构建更好和精心设计的API的真正能力。 这就是为什么作者建议你现在开始学习它,从本文本作者的角度来说,它最终将取代 REST。