78
阿里云最近在做活动,低至2折,有兴趣可以看看:
https://promotion.aliyun.com/...

为了保证的可读性,本文采用意译而非直译。

想阅读更多优质文章请猛戳GitHub博客,一年百来篇优质文章等着你!

今天最常讨论的术语之一是 API,很多人不知道 API 到底是什么,API 是 Application Programming Interface(应用程序编程接口)。顾名思义,它是一些预先定义的函数,目的是提供应用程序与开发人员基于某软件或硬件得以访问一组例程的能力,而又无需访问源码,或理解内部工作机制的细节。

你可以将 API 想象成一个酒保。你向酒保要一杯酒,他们会给你想要的。简单,但是传统的 REST API 有什么问题的呢?

自从现代 Web 出现以来,构建 Api 并不像听起来那么困难,但是学习和理解 Api 才是比较难。开发人员是大多数据使用你的 API 来构建某些内容或仅使用数据。所以你的 API 应该尽可能的简洁和直观, 好的 API 是非常容易使用和学习的。直观,在开始设计 API 时常要记住的一点。

我们已经使用 REST 构建 Api 很长时间了。随之而来的也有一些问题,在使用 REST 设计构建 API 时,你会遇到以下问题:

  • 涉及很多端
  • 于开发人员来说,学习和理解你的 API 要困难得多

* 信息的获取有多有少

为了解决这些问题,Facebook 创建了 GraphQL。现在,作者认为 GraphQL 是构建 Api 的最佳方式。这篇文章将告诉你为什么要学习了解一下 GraphQL。

什么是 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 中的第一个查询,更改和订阅,打开界面如下:

clipboard.png

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

模式(Schema)

GraphQL 有自己的语言类型,用于编写模式。 这是一种人类可读的模式语法,称为规范与描述语言(SDL)。无论使用何种技术,SDL 都是相同的 - 你可以将其用于你想要的任何语言或框架。

这种模式语言非常有用,因为它更直观的看出 API 具有哪些类型,一看到 API 就知道怎么使用。

类型(Type)

类型是 GraphQL 最重要的特性之一。类型是表示 API 外观的自定义对象。例如,如果你正在构建一个社交媒体应用程序,那么你的 API 应该具有诸如文章、用户、赞、组等类型。

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

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

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

每个用户都将拥有一个 ID,因此为其提供了 ID 类型。 用户也会有一个 nameemail,所以给它一个字符串类型和一个 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
  }
}

clipboard.png

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
  }
}

clipboard.png

subscription (订阅)

如我之前所说,订阅是你与服务器保持实时连接的方式。这意味着无论何时在服务器中发生事件,并且每当调用该事件时,服务器都会将相应的数据发送到客户端。

通过订阅,你可以让你的应用在不同的用户之间保持更新。

图片描述

基本订阅是这样的:(sample.graphql )

subscription {
  users {
    id
    name
    email
    age
  }
}

你会说它非常类似于查询,是的, 但它的工作方式不同。

当服务器中发生更新时,服务器将运行订阅中指定的 GraphQL 查询,并向客户机发送一个新更新的结果。

在这篇文章中,我们不打算讨论订阅,但是如果你想阅读更多关于订阅的信息,请单击这里

总结

如你所见,GraphQL 是一项非常强大的新技术。 它为我们提供了构建更好和精心设计的API的真正能力。 这就是为什么作者建议你现在开始学习它,从本文本作者的角度来说,它最终将取代 REST。


原文:

https://medium.freecodecamp.o...

你的点赞是我持续分享好东西的动力,欢迎点赞!

交流

干货系列文章汇总如下,觉得不错点个Star,欢迎 加群 互相学习。

https://github.com/qq44924588...

我是小智,公众号「大迁世界」作者,对前端技术保持学习爱好者。我会经常分享自己所学所看的干货,在进阶的路上,共勉!

关注公众号,后台回复福利,即可看到福利,你懂的。

clipboard.png


王大冶
68k 声望104.9k 粉丝