如何在 Node.js 中设置 GraphQL API 服务器

作者选择了 自由和开源基金作为 写给捐款计划的一部分接受捐款。

介绍

An Introduction to GraphQL中,您了解到 GraphQL 是一个开源查询语言和 API 运行时间,用于解决常见于传统 REST API 系统的问题。

开始了解所有 GraphQL 片段如何相匹配的一个好方法是创建一个 GraphQL API 服务器. 虽然 Apollo GraphQL是一个受众多大型公司喜爱的商业 GraphQL 实现,但它不是创建自己的 GraphQL API 服务器的先决条件。

在本教程中,您将创建一个 Express API 服务器在 Node.js中,为 GraphQL 终端提供服务。您还将基于 GraphQL 类型系统构建一个 GraphQL 方案,包括查询和突变等操作,以及解决函数来生成任何请求的响应。

前提条件

要遵循本教程,您将需要:

设置 Express HTTP 服务器

第一步是设置 Express 服务器,您可以在写任何 GraphQL 代码之前完成。

在新项目中,您将安装expresscors并使用npm install命令:

1npm install express cors

Express将是您的服务器的框架,它是 Node.js 的 Web 应用程序框架,旨在构建 API。 CORS包,即 Cross-Origin Resource Ssharing middleware,将允许您从浏览器轻松访问该服务器。

您还可以将 Nodemon 安装为 dev 依赖:

1npm install -D nodemon

Nodemon是帮助开发基于节点的应用程序的工具,通过在检测到目录中的文件更改时自动重新启动应用程序。

安装这些包将创建node_modulespackage.json,列出了两个依赖和一个 dev 依赖。

使用nano或您最喜欢的文本编辑器,打开package.json进行编辑,这将看起来像这样:

 1[label package.json]
 2{
 3  "dependencies": {
 4    "cors": "^2.8.5",
 5    "express": "^4.17.3"
 6  },
 7  "devDependencies": {
 8    "nodemon": "^2.0.15"
 9  }
10}

您将在此时将添加一些其他字段. 对 package.json 进行以下突出更改:

 1[label package.json]
 2{
 3  "main": "server.js",
 4  "scripts": {
 5    "dev": "nodemon server.js"
 6  },
 7  "dependencies": {
 8    "cors": "^2.8.5",
 9    "express": "^4.17.3"
10  },
11  "devDependencies": {
12    "nodemon": "^2.0.15"
13  },
14  "type": "module"
15}

您将为服务器创建一个文件在server.js,因此您将指向server.js

为了使在服务器上更容易开发,您还可以创建一个名为dev的脚本,该脚本将运行nodemon server.js

最后,您添加模块类型,以确保您可以在整个代码中使用导入陈述,而不是使用默认的CommonJS要求

保存并关闭文件,当你完成。

接下来,创建一个名为server.js的文件,在其中,你将创建一个简单的Express服务器,听到端口4000,并发送一个请求,说你好,GraphQL!

 1[label server.js]
 2import express from 'express'
 3import cors from 'cors'
 4
 5const app = express()
 6const port = 4000
 7
 8app.use(cors())
 9app.use(express.json())
10app.use(express.urlencoded({ extended: true }))
11
12app.get('/', (request, response) => {
13  response.send('Hello, GraphQL!')
14})
15
16app.listen(port, () => {
17  console.log(`Running a server at http://localhost:${port}`)
18})

此代码块创建了一个基本的 HTTP 服务器与 Express. 通过召唤 express 函数,你创建了一个 Express 应用程序. 设置了 CORS 和 JSON 的一些基本设置后,你将定义应该用GET 请求发送给 root (/) 使用 app.get('/')。

保存并关闭文件,当你完成。

现在您可以运行命令来启动 Node 服务器:

1npm run dev

如果您在浏览器中访问http://localhost:4000或运行curl http://localhost:4000命令,您将看到它返回Hello, GraphQL!表示Express服务器正在运行。

设置 GraphQL HTTP 服务器中间件

在本节中,您将开始将 GraphQL 架构集成到基本的 Express 服务器中,您将通过定义架构、解析器和连接到数据存储来做到这一点。

要开始将 GraphQL 集成到 Express 服务器中,您将安装三个包: graphqlexpress-graphql@graphql-tools/schema

1npm install graphql@14 express-graphql @graphql-tools/schema
  • graphql: 用于 GraphQL 的 JavaScript 引用实现
  • express-graphql: 用于 GraphQL 的 HTTP 服务器中间软件
  • @graphql-tools/schema: 一组用于更快的 GraphQL 开发的实用程序

您可以将这些包导入到「server.js」文件中,通过添加突出的行:

1[label server.js]
2import express from 'express'
3import cors from 'cors'
4import { graphqlHTTP } from 'express-graphql'
5import { makeExecutableSchema } from '@graphql-tools/schema'
6
7...

下一步是创建一个可执行的 GraphQL 方案。

为了避免设置数据库的负担,您可以使用内存存储器来查询 GraphQL 服务器将查询的数据。您可以创建一个具有数据库值的数据对象。

 1[label server.js]
 2import express from 'express'
 3import cors from 'cors'
 4import { graphqlHTTP } from 'express-graphql'
 5import { makeExecutableSchema } from '@graphql-tools/schema'
 6
 7const data = {
 8  warriors: [
 9    { id: '001', name: 'Jaime' },
10    { id: '002', name: 'Jorah' },
11  ],
12}
13
14...

这里的数据结构代表了一个名为战士的数据库表,它有两个行,由JaimeJorah条目表示。

<$>[注] **注:**使用真正的数据存储不在本教程的范围内。通过缩放器来访问和操纵 GraphQL 服务器中的数据。这可以通过手动连接到数据库,通过像 Prisma这样的 ORM来完成。 Asynchronous resolvers通过解决器的背景来实现这一点。对于本教程的其余部分,我们将使用数据变量来表示数据存储值 <$>

有了安装的包和一些数据,您现在将创建一个方案,通过描述可供查询的数据来定义API。

图形图表

现在你已经有了一些基本数据,你可以开始为API创建一个原始方案,以获得开始使用 GraphQL 终端所需的最小代码量. 这个方案旨在复制一些可以用于幻想角色扮演游戏的内容,其中有角色具有战士,魔法师和治愈者等角色。

一个 GraphQL 方案依赖于一个 类型系统。有一些内置类型,你也可以创建自己的类型. 对于这个示例,你将创建一个名为Warrior的新类型,并给它两个字段:idname

1type Warrior {
2  id: ID!
3  name: String!
4}

idID类型,而名称字符串类型,这些都是内置的尺度或原始类型。

您所需要的唯一额外的信息是基本的Query类型,这是 GraphQL 查询的入口点,我们将战士定义为一系列的战士类型。

1type Query {
2  warriors: [Warrior]
3}

有了这两种类型,你有一个有效的架构,可以在GraphQL HTTP中间软件中使用。最终,你在这里定义的架构将被转移到由graphql-tools提供的makeExecutableSchema函数中,作为typeDefs

  • typeDefs:一个 GraphQL 架构语言字符串
  • resolvers:呼吁执行一个字段并产生值的函数

server.js中,导入依赖之后,创建一个typeDefs变量,并将 GraphQL 方案分配为字符串,如下所示:

 1[label server.js]
 2...
 3
 4const data = {
 5  warriors: [
 6    { id: '001', name: 'Jaime' },
 7    { id: '002', name: 'Jorah' },
 8  ],
 9}
10
11const typeDefs = `
12type Warrior {
13  id: ID!
14  name: String!
15}
16
17type Query {
18  warriors: [Warrior]
19}
20`
21
22...

现在你已经定义了你的数据集和方案,分别为数据typeDefs

GraphQL 解析器函数

Resolvers是为 GraphQL 服务器生成响应函数的集合,每个解决函数有四个参数:

  • obj:这里不需要使用的母体对象,因为它已经是根或顶级对象
  • args:任何给该字段提供的 GraphQL 参数
  • context: 所有解析器之间共享的状态,通常是数据库连接
  • info: 附加信息

在这种情况下,您将为根类型Query创建解析器,并返回战士的值。

要开始使用此示例服务器,请通过将突出的行添加到server.js来传输本节中早些时候的内存数据库:

 1[label server.js]
 2...
 3
 4const typeDefs = `
 5type Warrior {
 6  id: ID!
 7  name: String!
 8}
 9
10type Query {
11  warriors: [Warrior]
12}
13`
14
15const resolvers = {
16  Query: {
17    warriors: (obj, args, context, info) => context.warriors,
18  },
19}
20
21...

您现在已经添加了一个解决函数,称为战士,将从背景返回战士背景是您的数据库输入点将被包含在那里,对于这个特定的实现,它将是包含您的内存数据库的数据变量。

每个单独的解析函数有四个参数:obj、args、context、info. 目前我们方案中最有用的和最相关的参数是context,这是解析器共享的对象,常被用作GraphQL服务器与数据库之间的连接。

最后,随着typeDefsresolvers的所有设置,您有足够的信息来创建一个可执行的方案。

 1[label server.js]
 2...
 3
 4const resolvers = {
 5  Query: {
 6    warriors: (obj, args, context, info) => context.warriors,
 7  },
 8}
 9
10const executableSchema = makeExecutableSchema({
11  typeDefs,
12  resolvers,
13})
14
15...

makeExecutableSchema函数创建了一个完整的方案,您可以将其传输到 GraphQL 终端。

现在,将当前返回 Hello, GraphQL! 的默认根端点替换为以下 /graphql 端点,添加突出的行:

 1[label server.js]
 2...
 3
 4const executableSchema = makeExecutableSchema({
 5  typeDefs,
 6  resolvers,
 7})
 8
 9app.use(
10  '/graphql',
11  graphqlHTTP({
12    schema: executableSchema,
13    context: data,
14    graphiql: true,
15  })
16)
17
18...

使用graphqlHTTP中间软件需要通过方案和一个背景,在这种情况下,这是你的模仿数据存储。

您现在拥有开始服务端点所需的一切,您的server.js代码应该是这样的:

 1[label server.js]
 2import express from 'express'
 3import cors from 'cors'
 4import { graphqlHTTP } from 'express-graphql'
 5import { makeExecutableSchema } from '@graphql-tools/schema'
 6
 7const app = express()
 8const port = 4000
 9
10// In-memory data store
11const data = {
12  warriors: [
13    { id: '001', name: 'Jaime' },
14    { id: '002', name: 'Jorah' },
15  ],
16}
17
18// Schema
19const typeDefs = `
20type Warrior {
21  id: ID!
22  name: String!
23}
24
25type Query {
26  warriors: [Warrior]
27}
28`
29
30// Resolver for warriors
31const resolvers = {
32  Query: {
33    warriors: (obj, args, context) => context.warriors,
34  },
35}
36
37const executableSchema = makeExecutableSchema({
38  typeDefs,
39  resolvers,
40})
41
42app.use(cors())
43app.use(express.json())
44app.use(express.urlencoded({ extended: true }))
45
46// Entrypoint
47app.use(
48  '/graphql',
49  graphqlHTTP({
50    schema: executableSchema,
51    context: data,
52    graphiql: true,
53  })
54)
55
56app.listen(port, () => {
57  console.log(`Running a server at http://localhost:${port}`)
58})

保存并关闭文件,当你完成。

现在,您应该能够进入http://localhost:4000/graphql,并使用 GraphiQL IDE探索您的方案。

您的 GraphQL API 现在基于您在本节中创建的方案和解析器完成,在下一节中,您将使用 GraphiQL IDE 来帮助您解析和理解方案。

使用 GraphiQL IDE

由于您将graphiql选项作为true应用到 GraphQL 中间软件中,您可以访问 GraphiQL 集成开发环境 (IDE)。

GraphiQL 是用于编写、验证和测试 GraphQL 查询的浏览器中的工具,现在您可以测试您的 GraphQL 服务器,以确保它返回正确的数据。

战士进行查询,请求idname属性. 在您的浏览器中,将以下行添加到 GraphiQL 的左面窗格:

1{
2  warriors {
3    id
4    name
5  }
6}

请按一下左上角的 **Play 箭头,然后在右边看到 JSON 中的返回值:

1[secondary_label Output]
2{
3  "data": {
4    "warriors": [
5      { "id": "001", "name": "Jaime" },
6      { "id": "002", "name": "Jorah" }
7    ]
8  }
9}

如果您删除查询中的一个字段,则会看到返回值相应的变化,例如,如果您只想要查找名称字段,则可以这样写查询:

1{
2  warriors {
3    name
4  }
5}

现在你的答案将是这样的:

1[secondary_label Output]
2{
3  "data": {
4    "warriors": [{ "name": "Jaime" }, { "name": "Jorah" }]
5  }
6}

仅查询所需的字段的能力是 GraphQL 的强大方面之一,这使其成为一个 客户端驱动的语言。

在 GraphiQL 中,如果您点击 Docs 向右,它将扩展标记为 Documentation Explorer 的侧面栏。

现在您的 API 已经完成,您已经探索了如何从 GraphiQL 使用它,下一步将是从客户端向您的 GraphQL API 提出实际请求。

从客户端查询 GraphQL API

REST API一样,客户端可以通过在网络上进行 HTTP 请求来与 GraphQL API 进行通信. 由于您可以使用内置的浏览器 API 如fetch来创建网络请求,您也可以使用fetch来查询 GraphQL。

对于一个非常基本的例子,在一个index.html文件中创建一个 HTML 骨骼,并使用一个<pre>标签:

 1[label index.html]
 2<!DOCTYPE html>
 3<html lang="en">
 4  <head>
 5    <meta charset="utf-8" />
 6    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
 7
 8    <title>GraphQL Client</title>
 9  </head>
10
11  <pre><!-- data will be displayed here --></pre>
12
13  <body>
14    <script>
15      // Add query here
16    </script>
17  </body>
18</html>

脚本标签中,创建一个非同步函数,向 GraphQL API 发送一个POST请求:

 1[label index.html]
 2...
 3<body>
 4    <script>
 5      async function queryGraphQLServer() {
 6        const response = await fetch('http://localhost:4000/graphql', {
 7          method: 'POST',
 8          headers: {
 9            'Content-Type': 'application/json',
10          },
11          body: JSON.stringify({
12            query: '{ warriors { name } }',
13          }),
14        })
15        const data = await response.json()
16    			
17        // Append data to the pre tag
18        const pre = document.querySelector('pre')
19        pre.textContent = JSON.stringify(data, null, 2) // Pretty-print the JSON
20      }
21
22      queryGraphQLServer()
23    </script>
24  </body>
25...

内容类型标题必须设置为应用程序/json,并将查询作为字符串传输到体内,脚本将调用函数来提出请求,并将答案设置为预先标签。

这里是完整的index.html代码。

 1[label index.html]
 2<!DOCTYPE html>
 3<html lang="en">
 4  <head>
 5    <meta charset="utf-8" />
 6    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
 7
 8    <title>GraphQL</title>
 9  </head>
10
11  <pre></pre>
12
13  <body>
14    <script>
15      async function queryGraphQLServer() {
16        const response = await fetch('http://localhost:4000/graphql', {
17          method: 'POST',
18          headers: {
19            'Content-Type': 'application/json',
20          },
21          body: JSON.stringify({
22            query: '{ warriors { name } }',
23          }),
24        })
25        const data = await response.json()
26
27        const pre = document.querySelector('pre')
28        pre.textContent = JSON.stringify(data, null, 2) // Pretty-print the JSON
29      }
30
31      queryGraphQLServer()
32    </script>
33  </body>
34</html>

保存并关闭文件,当你完成。

现在,当您在浏览器中查看 index.html 文件时,您将看到向 http://localhost:4000/graphql 终端发出的网络请求,该终端将与数据一起返回 200

如果您的请求顺利完成,并且您收到来自 GraphQL API 的数据的200响应,恭喜您!您创建了您的第一个 GraphQL API 服务器。

结论

在本教程中,您使用了 Node.js 中的 Express 框架创建了一个 GraphQL API 服务器. 该 GraphQL 服务器由一个单一的 `/graphql' 终端组成,可以处理传入请求来查询数据存储。

希望这篇文章有助于解密GraphQL,并开辟了与GraphQL可以实现什么的新想法和可能性. 有许多工具可以帮助您处理与GraphQL合作的更复杂方面,如身份验证,安全和缓存,但学习如何以最简单的方式设置API服务器应该帮助您了解GraphQL的基本知识。

本教程是我们如何使用 GraphQL 管理数据(https://www.digitalocean.com/community/tutorial_series/how-to-manage-data-with-graphql)系列的一部分,它涵盖了使用 GraphQL 的基本知识。

Published At
Categories with 技术
comments powered by Disqus