GraphQL 的入门指南 转

转自:https://segmentfault.com/a/1190000017851838

什么是 GraphQL

GraphQL 是 Facebook 开发的一种开源查询语言。它为我们提供了一种更有效的设计、创建和使用 Api的方法。从根本上说,它是 REST 的替代品。

GraphQL有很多特性,比如:

  1. GraphQL查询总是能准确获得你想要的数据,不多不少,所以返回的结果是可预测的, 不再像你使用 REST 那样过度获取信息。
  2. 它为我们提供了同一个端,对于同一个 API,没有版本2或版本3。给 GraphQL API 添加字段和类型而无需影响现有查询,老旧字段可以废弃,从工具中隐藏。
  3. 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 类型,我们应该有一些 nameemail 和 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中,有三个主要概念:

  1. query (查询) — 从服务器获取数据的方式。
  2. mutation (更改) — 修改服务器上的数据并获取更新数据的方法(创建、更新、删除)。
  3. 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
  }
}

它应该返回给你我们所有的用户。

clipboard.png

如果想返回特定的用户:

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。

点赞

发表回复

电子邮件地址不会被公开。必填项已用 * 标注