使用 Node.js API 探索 GraphQL





5.00/5 (1投票)
如何使用 Node.js 和 Express 实现 GraphQL 服务器
GraphQL 是一种用于应用程序编程接口 (API) 的查询语言。简单来说,它是一种定义你所需数据结构的语法。
我们已经有了 REST API,那么 GraphQL 有什么不同呢?与 REST API 一样,GraphQL 是一种无状态、客户端独立的用于数据交换的 API。然而,它的优势在于更高的灵活性。
在定制 API 调用方面,REST API 存在一些严重的性能问题。这通常会影响到应用程序的移动用户,导致内容加载缓慢。这通常是由于返回了除所需数据之外的、用处很少或没有用处的数据所花费的时间造成的。
为了解决加载时间过长的问题,开发人员必须为每个请求的数据结构定义新的端点,或者使用请求参数,这可能会让前端开发人员在发送请求时感到困惑,因为他们可能会错过传递正确的请求参数或混淆参数的使用。GraphQL 通过只有一个单一的端点和一个允许将请求的数据作为查询传递的结构来解决这个问题。
REST API 拥有庞大的用户群,但 GraphQL 正在获得普及。由于其性能的提高,一些大型公司 — 如 Shopify 和 GitHub — 已经开始为开发人员提供 GraphQL API。
在本文中,我们将向您展示如何使用 Node.js 和 Express 实现 GraphQL 服务器。我们将介绍设置项目、创建简单的 GraphQL 服务器、构建复杂的模式以及创建突变。您可以使用 此处 的存储库来获取我们将使用的代码。
GraphQL 如何工作
GraphQL 只需要一个单一的端点。该端点将带有数据定制的查询作为请求体,该请求体可以根据所请求数据的需求动态变化。
除了这一个端点,我们只会发送一种类型的请求:向 '/graphql' URL 发送一个 `POST` 请求。这是因为使用 `POST` 请求,您可以添加一个请求体,其中包含要执行的所有其他数据操作的查询。
GraphQL 查询的结构
一个典型的 GraphQL 查询包含三个部分:操作类型、操作端点和请求的字段
{
*operation type* {
*endpoint* {
*requested fields*
}
}
}
操作类型本身有三种类型
- 查询 (Query):用于检索特定数据。类似于 "
GET
" 请求。 - 突变 (Mutation):用于操作数据。类似于 "
POST
"、"PUT
"、"PATCH
" 或 "DELETE
"。 - 订阅 (Subscription):用于通过
WebSocket
建立实时连接。
理论知识讲得够多了。让我们动手写代码吧!
项目设置
您可以使用任何 Web 服务器编程语言来实现 GraphQL 服务器,例如 Java、Ruby、Python 和 C# 等。
在本演示中,我将使用 Node.js 和 Express 来设置 GraphQL 服务器。如果您想通过 Node.js 来学习本文,并且尚未在您的系统上安装它,您可以从 官方 Node.js 网站 下载。
打开您喜欢的命令提示符,并使用以下命令检查 Node.js 和 NPM 是否已正确安装
node -v
npm -v
如果看到它们各自的版本号,则表示已成功安装。
创建一个具有适当名称的新空项目文件夹
mkdir graphql-demo
进入该目录,并通过使用以下命令创建一个 'package.json' 文件来初始化您的 Node.js 项目
npm init
在项目目录中创建一个新的 server.js 文件,使用以下命令
适用于 Windows
type nul > server.js
适用于 Mac 和 Linux
touch server.js
通过以下命令将所需的 NPM 包 graphql、express 和 `express-graphql` 添加到项目中
npm install graphql express express-graphql –save
现在我们已经设置好了项目,可以开始实现我们的第一个 GraphQL 服务器了。
创建简单的 GraphQL 服务器
为了处理 GraphQL 查询,我们需要一个定义操作类型和 API 端点的模式。我们还需要一个解析器来映射请求中的端点。不使用 Express 服务器,使用 GraphQL 的简单 "Hello World!
" 程序如下所示
var { graphql, buildSchema } = require('graphql');
// Construct a schema, using GraphQL schema language
var schema = buildSchema(`
type Query {
hello: String
}
`);
// The root provides a resolver function for each API endpoint
var root = {
hello: () => {
return 'Hello world!';
},
};
// Run the GraphQL query '{ hello }' and print out the response
graphql(schema, '{ hello }', root).then((response) => {
console.log(response);
});
模式定义了客户端如何访问数据。它会在每次 API 调用时进行验证,只有在验证成功时才返回数据,否则会返回错误。
解析器由将操作映射到查询中定义的端点组成。随着查询的增长,解析器也会增长。
运行以下命令以查看 GraphQL 响应
node server.js
如果您得到以下响应,那么您刚刚运行了您的第一个 GraphQL 查询
{data: { hello: ‘Hello world!’} }
让我们尝试与上面相同的程序,但这次,我们将启动一个 Express 服务器。将您的 server.js 代码更改为以下代码
var express = require('express');
var { graphqlHTTP } = require('express-graphql');
var { buildSchema } = require('graphql');
// Construct a schema, using GraphQL schema language
var schema = buildSchema(`
type Query {
hello: String
}
`);
// The root provides a resolver function for each API endpoint
var root = {
hello: () => {
return 'Hello world!';
},
};
var app = express();
app.use('/graphql', graphqlHTTP({
schema: schema,
rootValue: root,
graphiql: true,
}));
app.listen(4000);
console.log('Running a GraphQL API server at https://:4000/graphql');
Express 启动一个带有 GraphQL 的服务器,并将 'graphiql
' 设置为 'true
'。然后它会打开一个交互式的图形界面,用于尝试 GraphQL 查询。使用以下命令启动服务器
node server.js
打开您喜欢的浏览器并导航到 https://:4000/graphql。您将能够看到一个允许您输入查询的界面
恭喜 — 您刚刚使用 Express 服务器执行了一个 GraphQL 查询!
构建复杂的模式
让我们更新模式以获取示例文本数据,如下所示
var heroes = [
{
id: 1,
name: 'Iron Man',
age: '40',
ability: 'Flying, Rockets'
},
{
id: 2,
name: 'Dr. Strange',
age: '30',
ability: 'Magic'
},
{
id: 3,
name: 'Hulk',
age: '45',
ability: 'Anger'
},
{
id: 4,
name: 'Ant man',
age: '26',
ability: 'Shrink'
},
{
id: 5,
name: 'Black Widow',
age: '25',
ability: 'Spy'
}
];
除了使用整数和字符串等基本类型,我们还可以定义自己的类型并在其他查询参数中使用它
var schema = buildSchema(`
type Query {
hero(id: Int!): Person
},
type Person {
id: Int
name: String
age: Int
ability: String
}
`);
这里,‘Person
’ 是为参数 ‘hero
’ 定义的用户自定义类型。我们必须像下面这样更新解析器
var root = {
hero: getHero, // Resolver function to return hero with specific id
};
让我们再次使用以下命令启动服务器
node server.js
导航到 https://:4000/graphql 并输入以下查询
query getHero($heroId: Int!) {
hero(id: $heroId) {
id
name
age
ability
}
}
如果您运行查询,您将收到以下错误
{
"errors": [
{
"message": "Variable /"$heroId\" of required type \"Int!\" was not provided.",
"locations": [
{
"line": 1,
"column": 15
}]
}]
}
正如错误所示,我们缺少变量 $heroId
。让我们通过点击 GraphQL 界面左下角的 "Query Variables" 选项卡来快速创建一个变量。
现在,如果您再次按运行,您可以看到我们作为参数传递了 id 的特定英雄的响应数据
使用突变
我们已经看到了如何使用 GraphQL 检索数据。现在,让我们看看如何使用另一种称为突变的操作类型来更新数据。
通过定义一个突变来更新构建的模式
var schema = buildSchema(`
type Query {
hero(id: Int!): Person
},
type Person {
id: Int
name: String
age: Int
ability: String
}
type Mutation {
updateHero(id: Int!, name: String!, ability: String): Person
}
`);
我们可以看到突变接受三个参数:‘id
’、‘name
’ 和 ‘ability
’,并返回 ‘Person
’ 类型的数据。我们还需要创建一个新函数来更新数据。我们还需要更新解析器以指向该函数
// Updates a hero and returns new hero details
var updateHero = function ({ id, name, ability }) {
heros.map(hero => {
if (hero.id === id) {
hero.name = name;
hero.age = ability;
return hero;
}
});
return heros.filter(hero => hero.id === id)[0];
}
// Root resolver
var root = {
hero: getHero, // Resolver function to return hero with specific id
updateHero: updateHero
};
让我们再次使用以下命令运行服务器文件
node server.js
导航到 https://:4000/graphql 并输入以下查询
mutation updateHero($id: Int!, $name: String!, $ability: String) {
updateHero(id: $id, name:$name, ability: $ability){
name
ability
}
}
同时,在 "Query Variables" 部分定义必需的参数
{
"id": 1,
"name": "Captain America",
"ability": "Shield"
}
点击运行。我们现在可以看到更新后的 ‘hero
’ 数据
{
"data": {
"updateHero": {
"name": "Captain America",
"
Voilà!您刚刚使用 GraphQL 更新了您的数据。
如何在自己的应用程序中减少加载时间
GraphQL
使开发人员能够精确地请求他们所需的内容,不多也不少。这有助于减少不必要的 API 调用,从而缩短应用程序的加载时间。
虽然 REST API 由于市场普及性而超越 GraphQL,但 GraphQL 正在获得支持。一旦支持 GraphQL 的工具广泛可用,它将超越 REST。在 Shopify 和 GitHub 等主要组织的 Double 的支持下,它正在这条路上。
要了解更多信息,请参阅 GraphQL 文档。
本文最初作为客座投稿出现在ContentLab。