GraphQL - 快速指南


GraphQL - 简介

GraphQL 是一种开源服务器端技术,由 Facebook 开发,用于优化 RESTful API 调用。它是一个执行引擎和一种数据查询语言。在本章中,我们讨论使用 GraphQL 的优点。

为什么选择 GraphQL

RESTful API 遵循清晰且结构良好的面向资源的方法。然而,当数据变得更加复杂时,路线就会变得更长。有时不可能通过单个请求获取数据。这就是 GraphQL 派上用场的地方。GraphQL 使用强大的查询语法以图形的形式构建数据,用于遍历、检索和修改数据。

以下是使用 GraphQL 查询语言的优点 -

询问你想要什么 - 并得到它

将 GraphQL 查询发送到您的 API,并准确获取您需要的内容。GraphQL 查询始终返回可预测的结果。使用 GraphQL 的应用程序快速且稳定。与 Restful 服务不同,这些应用程序可以限制应从服务器获取的数据。

以下示例将帮助您更好地理解这一点 -

让我们考虑一个具有id、firstName、lastNameCollegeName属性的业务对象Student。假设移动应用程序只需要获取firstNameid。如果我们设计一个像/api/v1/students这样的 REST 端点,它将最终获取学生对象的所有字段的数据。这意味着 RESTful 服务过度获取数据。这个问题可以通过使用GraphQL来解决。

考虑下面给出的 GraphQL 查询 -

{
   students {
      id
      firstName
   }
}

这将仅返回 id 和 Firstname 字段的值。该查询不会获取学生对象的其他属性的值。上面所示的查询的响应如下所示 -

{
   "data": {
      "students": [
         {
            "id": "S1001",
            "firstName": "Mohtashim"
         },
         {
            "id": "S1002",
            "firstName": "Kannan"
         }
      ]
   }
}

在单个请求中获取许多资源

GraphQL 查询有助于顺利检索关联的业务对象,而典型的 REST API 需要从多个 URL 加载。GraphQL API 可在单个请求中获取应用程序所需的所有数据。即使在缓慢的移动网络连接上,使用 GraphQL 的应用程序也可以很快。

让我们考虑另一个业务对象,College,它具有以下属性:名称和位置。Student业务对象与College对象有关联关系。如果我们要使用 REST API 来获取学生及其大学的详细信息,我们最终将向服务器发出两个请求,例如/api/v1/students/api/v1/colleges。这将导致每个请求的数据获取不足。因此,移动应用程序被迫多次调用服务器才能获取所需的数据。

但是,移动应用程序可以使用 GraphQL 在单个请求中获取 Student 和 College 对象的详细信息。

以下是用于获取数据的 GraphQL 查询 -

{
   students{
      id
      firstName
      lastName
      college{
         name
         location
      }
   }
}

上述查询的输出恰好包含我们请求的字段,如下所示 -

{
   "data": {
      "students": [
         {
            "id": "S1001",
            "firstName": "Mohtashim",
            "lastName": "Mohammad",
            "college": {
               "name": "CUSAT",
               "location": "Kerala"
            }
         },
         
         {
            "id": "S1002",
            "firstName": "Kannan",
            "lastName": "Sudhakaran",
            "college": {
               "name": "AMU",
               "location": "Uttar Pradesh"
            }
         },
         
         {
            "id": "S1003",
            "firstName": "Kiran",
            "lastName": "Panigrahi",
            "college": {
               "name": "AMU",
               "location": "Uttar Pradesh"
            }
         }
      ]
   }
}

描述类型系统的可能性

GraphQL 是强类型的,查询基于字段及其关联的数据类型。如果 GraphQL 查询中存在类型不匹配,服务器应用程序会返回清晰且有用的错误消息。这有助于客户端应用程序顺利调试并轻松检测错误。GraphQL 还提供客户端库,可以帮助减少显式数据转换和解析。

下面给出了学生学院数据类型的示例-

type Query {
   students:[Student]
}

type Student {
   id:ID!
   firstName:String
   lastName:String
   fullName:String
   college:College
}

type College {
   id:ID!
   name:String
   location:String
   rating:Float
   students:[Student]
}

使用强大的开发工具加快行动速度

GraphQL 为文档和测试查询提供了丰富的开发人员工具。GraphiQL 是一个优秀的工具,可以生成查询及其架构的文档。它还提供了一个查询编辑器,可以在构建查询时测试 GraphQL API 和智能代码完成功能。

GraphQL - 环境设置

在本章中,我们将了解 GraphQL 的环境设置。要执行本教程中的示例,您将需要以下内容 -

  • 运行 Linux、macOS 或 Windows 的计算机。

  • 网络浏览器,最好是最新版本的 Google Chrome。

  • 安装了最新版本的 Node.js。建议使用最新的 LTS 版本。

  • 安装了 VSCode 扩展 GraphQL 的 Visual Studio Code 或您选择的任何代码编辑器。

如何使用 Nodejs 构建 GraphQL 服务器

我们将通过详细的逐步方法使用 Nodejs 构建 GraphQL 服务器,如下所示 -

步骤 1 - 验证节点和 Npm 版本

安装 NodeJs 后,在终端上使用以下命令验证节点和 npm 的版本 -

C:\Users\Admin>node -v
v8.11.3

C:\Users\Admin>npm -v
5.6.0

步骤 2 - 创建项目文件夹并在 VSCode 中打开

项目的根文件夹可以命名为test-app。

按照以下说明,使用 Visual Studio 代码编辑器打开文件夹 -

C:\Users\Admin>mkdir test-app
C:\Users\Admin>cd test-app
C:\Users\Admin\test-app>code.

步骤 3 - 创建 package.json 并安装依赖项

创建一个 package.json 文件,其中包含 GraphQL 服务器应用程序的所有依赖项。

{
   "name": "hello-world-server",
   "private": true,
   "scripts": {
      "start": "nodemon --ignore data/ server.js"
   },
   
   "dependencies": {
      "apollo-server-express": "^1.4.0",
      "body-parser": "^1.18.3",
      "cors": "^2.8.4",
      "express": "^4.16.3",
      "graphql": "^0.13.2",
      "graphql-tools": "^3.1.1"
   },
   
   "devDependencies": {
      "nodemon": "1.17.1"
   }
}

使用下面给出的命令安装依赖项 -

C:\Users\Admin\test-app>npm install

步骤 4 - 在数据文件夹中创建平面文件数据库

在此步骤中,我们使用平面文件来存储和检索数据。创建文件夹 data 并添加两个文件Students.jsonColleges.json

以下是Colleges.json文件 -

[
   {
      "id": "col-101",
      "name": "AMU",
      "location": "Uttar Pradesh",
      "rating":5.0
   },
   
   {
      "id": "col-102",
      "name": "CUSAT",
      "location": "Kerala",
      "rating":4.5
   }
]

以下是Students.json文件 -

[
   {
      "id": "S1001",
      "firstName":"Mohtashim",
      "lastName":"Mohammad",
      "email": "mohtashim.mohammad@tutorialpoint.org",
      "password": "pass123",
      "collegeId": "col-102"
   },
   
   {
      "id": "S1002",
      "email": "kannan.sudhakaran@tutorialpoint.org",
      "firstName":"Kannan",
      "lastName":"Sudhakaran",
      "password": "pass123",
      "collegeId": "col-101"
   },
   
   {
      "id": "S1003",
      "email": "kiran.panigrahi@tutorialpoint.org",
      "firstName":"Kiran",
      "lastName":"Panigrahi",
      "password": "pass123",
      "collegeId": "col-101"
   }
]

第 5 步 - 创建数据访问层

我们需要创建一个加载数据文件夹内容的数据存储。在本例中,我们需要集合变量StudentsCollege。每当应用程序需要数据时,它就会使用这些集合变量。

在项目文件夹中创建文件 db.js,如下所示 -

const { DataStore } = require('notarealdb');

const store = new DataStore('./data');

module.exports = {
   students:store.collection('students'),
   colleges:store.collection('colleges')
};

步骤 6 - 创建架构文件 schema.graphql

在当前项目文件夹中创建一个架构文件并添加以下内容 -

type Query  {
   test: String
}

第 7 步 - 创建解析器文件,resolvers.js

在当前项目文件夹中创建解析器文件并添加以下内容 -

const Query = {
   test: () => 'Test Success, GraphQL server is up & running !!'
}
module.exports = {Query}

步骤 8 - 创建 Server.js 并配置 GraphQL

创建服务器文件并配置 GraphQL,如下所示 -

const bodyParser = require('body-parser');
const cors = require('cors');
const express = require('express');
const db = require('./db');

const port = process.env.PORT || 9000;
const app = express();

const fs = require('fs')
const typeDefs = fs.readFileSync('./schema.graphql',{encoding:'utf-8'})
const resolvers = require('./resolvers')

const {makeExecutableSchema} = require('graphql-tools')
const schema = makeExecutableSchema({typeDefs, resolvers})

app.use(cors(), bodyParser.json());

const  {graphiqlExpress,graphqlExpress} = require('apollo-server-express')
app.use('/graphql',graphqlExpress({schema}))
app.use('/graphiql',graphiqlExpress({endpointURL:'/graphql'}))

app.listen(
   port, () => console.info(
      `Server started on port ${port}`
   )
);

第 9 步 - 使用 GraphiQL 运行应用程序并进行测试

验证项目 test-app 的文件夹结构如下 -

test-app /
   -->package.json
   -->db.js
   -->data
      students.json
      colleges.json
   -->resolvers.js
   -->schema.graphql
   -->server.js

运行命令 npm start 如下所示 -

C:\Users\Admin\test-app>npm start

服务器运行在 9000 端口,因此我们可以使用 GraphiQL 工具测试应用程序。打开浏览器并输入 URL http://localhost:9000/graphiql。在编辑器中输入以下查询 -

{
   Test 
}

来自服务器的响应如下 -

{
   "data": {
      "test": "Test Success, GraphQL server is running !!"
   }
}

环境设置.jpg

GraphQL - 架构

GraphQL 是描述 GraphQL 服务器Behave的规范。它是一组关于如何处理请求和响应的指南,例如支持的协议、服务器可以接受的数据格式、服务器返回的响应格式等。客户端向 GraphQL 发出的请求服务器称为查询。GraphQL 的另一个重要概念是其传输层不可知论。它可以与任何可用的网络协议(如 TCP、Websocket 或任何其他传输层协议)一起使用。它对数据库也是中性的,因此您可以将它与关系数据库或 NoSQL 数据库一起使用。

可以使用下面列出的三种方法中的任何一种来部署 GraphQL Server -

  • 连接数据库的 GraphQL 服务器
  • 集成现有系统的GraphQL服务器
  • 混合方法

具有连接数据库的 GraphQL Server

该架构有一个带有集成数据库的 GraphQL Server,通常可以用于新项目。收到查询后,服务器读取请求负载并从数据库中获取数据。这称为解决查询。返回给客户端的响应遵循官方 GraphQL 规范中指定的格式。

GraphQL Server 连接数据库

在上图中,GraphQL 服务器和数据库集成在单个节点上。客户端(桌面/移动)通过 HTTP 与 GraphQL 服务器通信。服务器处理请求,从数据库中获取数据并将其返回给客户端。

GraphQL Server 集成现有系统

这种方法对于拥有遗留基础设施和不同 API 的公司很有帮助。GraphQL 可用于统一现有系统中的微服务、遗留基础设施和第三方 API。

GraphQL Server 集成现有系统

在上图中,GraphQL API 充当客户端和现有系统之间的接口。客户端应用程序与 GraphQL 服务器通信,后者解析查询。

混合方法

最后,我们可以结合上述两种方法构建一个 GraphQL 服务器。在此架构中,GraphQL 服务器将解析收到的任何请求。它将从连接的数据库或集成的 API 检索数据。如下图所示 -

GraphQL 混合方法

GraphQL - 应用程序组件

本章讨论不同的 GraphQL 组件以及它们相互通信的方式。整个应用程序组件可以区分如下 -

  • 服务器端组件
  • 客户端组件

服务器端组件

GraphQL 服务器构成服务器端的核心组件,并允许解析来自 GraphQL 客户端应用程序的查询。Apollo Server 是 GraphQL 规范最常用的实现。其他服务器编程组件包括以下内容 -

先生。 服务器要点和描述
1

模式

GraphQL 模式是任何 GraphQL 服务器实现的中心,它描述了连接到它的客户端可用的功能。

2

询问

GraphQL 查询是从数据库或旧版 API 检索数据的客户端应用程序请求。

3

旋转变压器

解析器提供将 GraphQL 操作转换为数据的指令。他们通过定义解析器函数来解析对数据的查询。

客户端组件

下面给出的是客户端组件 -

先生。 工具及说明
1

GraphiQL

基于浏览器的界面,用于编辑和测试 GraphQL 查询和突变。

2

Apollo客户端

构建 GraphQL 客户端应用程序的最佳工具。与所有 javascript 前端集成良好。

下图显示了客户端-服务器架构。Web 服务器基于 NodeJs 和 Express 框架构建。ReactJS 应用程序(使用 Apollo 客户端库构建)或 GraphiQL 浏览器应用程序向 Apollo GraphQL Server 发出请求。将根据服务器中定义的模式来解析和验证查询。如果请求模式通过验证,则将执行关联的解析器函数。解析器将包含从 API 或数据库获取数据的代码。

客户端组件

GraphQL - 示例

在本章中,我们将创建一个简单的 API,它返回问候消息 HelloWorld,并使用 GraphiQL 访问它。

例子

本示例基于 NodeJS、Express 和 Apollo 服务器。我们将学习通过以下步骤将所有概念放在一起 -

第 1 步 - 设置 Express

ExpressJS 是一个 Web 应用程序框架,可帮助构建网站和 Web 应用程序。在此示例中,我们将在 Express 框架之上构建 GraphQL API。

下一步是创建文件夹hello-world-server并从终端导航到同一文件夹。添加package.json,并为包命名。由于该包仅在内部使用,因此我们可以将其声明为私有。

{
   "name":"hello-world-server",
   "private":true
}

安装 Express 服务器的依赖项,如下所示 -

C:\Users\Admin\hello-world-server>npm install express body-parser cors

body-parser是一个中间件包,可以帮助 Express 高效处理 HTTP Post 请求。cors是另一个处理跨域资源共享的中间件包。

在项目文件夹中创建一个server.js文件并在其中输入以下内容 -

const bodyParser = require('body-parser')
   const cors = require('cors')
   const express = require('express')
   const port = process.env.PORT|| 9000
   const app = express()
   
   //register middleware
   app.use(bodyParser.json() , cors())
   app.listen(port, () =>  console.log(`server is up and running at ${port}`)

要验证 Express 服务器是否已启动并运行,请在终端窗口中执行以下代码 -

C:\Users\Admin\hello-world-server>node server.js

服务器控制台中显示以下输出。这表明 Express 服务器正在端口 9000 上运行。

server is up and running at 9000

如果打开浏览器并输入 http://localhost:9000,您将看到以下屏幕 -

运行 Epress 服务器

要停止服务器,请按 Ctrl + C

步骤 2 - 安装 GraphQL 和 Apollo Server

现在 Express 已配置完毕,下一步是下载以下 GraphQL 依赖项 -

  • 图ql
  • graphql 工具
  • 阿波罗-服务器-express@1

我们将使用 Apollo 服务器 v1.0,因为它是一个稳定版本。键入以下命令来安装这些依赖项 -

C:\Users\Admin\hello-world-server>npm install graphql graphql-tools apollo-server-express@1

 我们可以通过检查之前创建的package.json文件来验证这些依赖项是否安装成功 。

{
   "name": "hello-world-server",
   "private": true,
   
   "dependencies": {
      "apollo-server-express": "^1.4.0",
      "body-parser": "^1.18.3",
      "cors": "^2.8.4",
      "express": "^4.16.3",
      "graphql": "^0.13.2",
      "graphql-tools": "^3.1.1"
   }
}

步骤 3 - 定义架构

GraphQL 模式定义了可以从服务中获取哪种类型的对象以及它具有哪些字段。可以使用GraphQL 架构定义语言来定义架构。现在,在server.js文件中添加以下代码片段-

// Adding Type Definitions
const typeDefinition = `
   type Query  {
      greeting: String
   }

此处,查询包含返回字符串值的greeting属性。

第 4 步 - 创建解析器

创建解析器的第一步是添加一些代码来处理问候语字段的请求。这是在解析器中指定的。解析器函数的结构必须与架构匹配。在server.js文件中添加以下代码片段 。

// Adding resolver
const  resolverObject = {
   Query : {
      greeting: () => 'Hello GraphQL  From TutorialsPoint !!'
   }
}

第二步是使用makeExecutableSchema绑定架构和解析器。该函数是在 graphql-tools 模块中预定义的。在server.js文件中添加以下代码片段  。

const {makeExecutableSchema} = require('graphql-tools')
const schema = makeExecutableSchema({typeDefs:typeDefinition, resolvers:resolverObject})

第 5 步 - 定义从 ReactJS/GraphiQL 应用程序获取数据的路由

在server.js文件中添加以下代码片段-

const {graphqlExpress, graphiqlExpress} = require('apollo-server-express')

   //create routes for graphql and graphiql
   app.use('/graphql',graphqlExpress({schema}))
   
   app.use('/graphiql',graphiqlExpress({endpointURL:'/graphql'}))

graphqlExpress函数有助于注册路由http://localhost:9000/ graphql。ReactJS应用程序可以使用此端点来查询数据。同样,graphqliExpress函数有助于注册路由http://localhost:9000/graphiql。GraphiQL 浏览器客户端将使用它来测试 API。

完整的 server.js 代码如下 -

const bodyParser = require('body-parser')
const cors = require('cors')
const express = require('express')
const port = process.env.PORT||9000
const app = express()

app.use(bodyParser.json() , cors())
const typeDefinition = `
type Query  {
   greeting: String
}`
const  resolverObject = {
   Query : {
      greeting: () => 'Hello GraphQL  From TutorialsPoint !!'
   }
}
const {makeExecutableSchema} = require('graphql-tools')

const schema = makeExecutableSchema({typeDefs:typeDefinition, resolvers:resolverObject})

const {graphqlExpress,graphiqlExpress} = require('apollo-server-express')

app.use('/graphql',graphqlExpress({schema}))
app.use('/graphiql',graphiqlExpress({endpointURL:'/graphql'}))
app.listen(port, () =>  console.log(`server is up and running ${port}`))

第 6 步 - 启动应用程序

使用 Node.js执行server.js,如下所示 -

C:\Users\Admin\hello-world-server>node server.js

第 7 步 - 测试 GraphQL API

打开浏览器并输入http://localhost:9000/graphiql。在 GraphiQL 的查询选项卡中,输入以下内容 -

{
   greeting
}

来自服务器的响应如下 -

{
   "data": {
      "greeting": "Hello GraphQL From TutorialsPoint !!"
   }
}

下图说明了响应 -

测试 GraphQL API

注意- 请确保使用 Apollo Server 版本 1.0。

GraphQL - 类型系统

GraphQL 是一种强类型语言。类型系统定义了可在 GraphQL 应用程序中使用的各种数据类型。类型系统有助于定义模式,这是客户端和服务器之间的契约。常用的 GraphQL 数据类型如下:

先生。 类型和描述
1

标量

存储单个值

2

目的

显示可以获取什么类型的对象

3

询问

其他特定类型的入口点类型

4

突变

数据操作的入口点

5

枚举

在您需要用户从规定的选项列表中进行选择的情况下很有用

标量类型

标量类型是只能存储单个值的原始数据类型。GraphQL 提供的默认标量类型是 -

  • Int - 有符号 32 位整数

  • Float - 有符号双精度浮点值

  • 字符串- UTF - 8 字符序列

  • 布尔值- 真或假

  • ID - 唯一标识符,通常用作获取对象的唯一标识符或用作缓存的键。

定义标量类型的语法如下 -

field: data_type

下面给出的代码片段定义了一个名为greeting 的字段,它返回字符串值。

greeting: String

对象类型

对象类型是模式中最常用的类型,表示一组字段。对象类型内的每个字段都映射到另一种类型,从而允许嵌套类型。换句话说,一个对象类型是由多个标量类型或对象类型组成的。

定义对象类型的语法如下:

type object_type_name
{
   field1: data_type
   field2:data_type 
   ....
   fieldn:data_type
}

您可以考虑以下代码片段 -

--Define an object type--

type Student {
   stud_id:ID
   firstname: String
   age: Int
   score:Float
}

--Defining a GraphQL schema--  

type Query
{
   stud_details:[Student]
}

上面给出的示例定义了一个对象数据类型 Student。根查询模式中的stud_details字段将返回Student对象的列表。

查询类型

GraphQL 查询用于获取数据。这就像在基于 REST 的 API 中请求资源一样。为了简单起见,查询类型是从客户端应用程序发送到 GraphQL 服务器的请求。GraphQL 使用架构定义语言 (SDL)来定义查询。查询类型是 GraphQL 中众多根级类型之一。

定义查询的语法如下 -

type Query {
   field1: data_type
   field2:data_type
   field2(param1:data_type,param2:data_type,...paramN:data_type):data_type
}

定义查询的示例 -

type Query  {
   greeting: String
}

突变类型

突变是发送到服务器以创建、更新删除数据的操作。这些类似于调用基于 REST 的 API 的 PUT、POST、PATCH 和 DELETE 动词。

变异是 GraphQL 中的根级数据类型之一。查询类型定义数据获取操作的入口点,而突变类型指定数据操作操作的入口点。

定义 Mutation 类型的语法如下 -

type Mutation {
   field1: data_type
   field2(param1:data_type,param2:data_type,...paramN:data_type):data_type 
}

例如,我们可以定义一个突变类型来添加一个新的学生,如下所示 -

type Mutation {
   addStudent(firstName: String, lastName: String): Student
}

枚举类型

枚举类似于标量类型。在字段值必须来自规定的选项列表的情况下,枚举非常有用。

定义 Enum 类型的语法是 -

type enum_name{
   value1
   value2
}

以下代码片段说明了如何定义枚举类型 -

type Days_of_Week{
   SUNDAY
   MONDAY
   TUESDAY
   WEDNESDAY
   THURSDAY
   FRIDAY
   SATURDAY
}

列表类型

列表可用于表示特定类型的值的数组。列表是使用类型修饰符 [] 来定义的,该修饰符包装了对象类型、标量和枚举。

以下语法可用于定义列表类型 -

field:[data_type]

下面的示例定义了一个列表类型 todos -

type Query {
   todos: [String]
}

不可空类型

默认情况下,每个核心标量类型都可以设置为 null。换句话说,这些类型可以返回指定类型的值,也可以没有值。要覆盖此默认值并指定必须定义字段,可以将感叹号 (!) 附加到类型。这确保了查询返回的结果中存在值。

以下语法可用于定义不可为空的字段 -

field:data_type!

在下面的示例中,stud_id被声明为必填字段。

type Student {
   stud_id:ID!
   firstName:String
   lastName:String
   fullName:String
   college:College
}

GraphQL - 架构

GraphQL 模式是任何 GraphQL 服务器实现的核心。它描述了连接到它的客户端应用程序可用的功能。我们可以使用任何编程语言来创建 GraphQL 模式并围绕它构建接口。

GraphQL 运行时定义了一个通用的基于图形的模式来发布它所代表的数据服务的功能。客户端应用程序可以在其能力范围内查询架构。这种方法将客户端与服务器解耦,并允许两者独立发展和扩展。

在本章中,我们使用 Apollo 服务器来执行 GraphQL 查询。graphql-tools 中的 makeExecutableSchema函数可帮助您绑定架构和解析器。

makeExecutableSchema 函数语法

makeExecutableSchema函数采用一个对象类型的参数 {} 使用此函数的语法如下 -

import { makeExecutableSchema } from 'graphql-tools';

const jsSchema = makeExecutableSchema({
   typeDefs,
   resolvers, // optional
   logger, // optional
   allowUndefinedInResolve = false, // optional
   resolverValidationOptions = {}, // optional
   directiveResolvers = null, // optional
   schemaDirectives = null,  // optional
   parseOptions = {},  // optional
   inheritResolversFromInterfaces = false  // optional
});	

先生。 参数及说明
1

类型定义

这是一个必需的参数。它将 GraphQL 查询表示为 UTF-8 字符串。

2

旋转变压器

这是一个可选参数(默认为空对象)。它具有处理查询的函数。

3

记录器

这是一个可选参数,可用于将错误打印到服务器控制台。

4

解析选项

这是一个可选参数,允许在将 typeDefs 指定为字符串时自定义解析。

5

允许未定义解析

默认情况下这是正确的。当设置为 false 时,如果解析函数返回未定义,则会引发错误。

6

解析器验证选项

这是一个可选参数,接受具有布尔属性的对象。

7

从接口继承解析器

这是一个可选参数,接受布尔参数来检查解析器对象继承。

插图

让我们创建一个简单的应用程序来理解这个模式。这将创建一个用于从服务器查询学生列表的架构。学生数据将存储在一个平面文件中,我们将使用名为notarealdb的节点模块来伪造数据库并从平面文件中读取。

第 1 步 - 下载并安装项目所需的依赖项

创建一个名为schema-app的文件夹。从终端将目录更改为 schema-app。然后,按照环境设置一章中说明的步骤 3 至 5 完成下载和安装过程。

第 2 步 - 创建架构

在项目文件夹schema-app中添加schema.graphql文件并添加以下代码 -

type Query {
   greeting:String
   students:[Student]
}

type Student {
   id:ID!
   firstName:String
   lastName:String
   password:String
   collegeId:String
}

模式的根将是查询类型。该查询有两个字段:greeting 和 Students,分别返回字符串和学生列表。Student 被声明为对象类型,因为它包含多个字段。ID 字段被声明为不可为空。

第 3 步 - 创建解析器

在项目文件夹中创建文件resolvers.js并添加以下代码 -

const db = require('./db')
const Query = {
   greeting:() => {
      return "hello from  TutorialsPoint !!!"
   },
   students:() => db.students.list()
}

module.exports = {Query}

这里的greeting和students是处理查询的解析器。学生解析器函数从数据访问层返回学生列表。要访问模块外部的解析器函数,必须使用module.exports导出 Query 对象。

第 4 步 - 运行应用程序

创建一个 server.js 文件并参考环境设置章节中的步骤 8。下一步是在终端中执行命令 npm start。服务器将在 9000 端口上启动并运行。在这里,我们使用 GraphiQL 作为客户端来测试应用程序。打开浏览器并输入 URL http://localhost:9000/graphiql

在编辑器中输入以下查询 -

{
   greeting
   students {
      id
      firstName
      lastName
   }
}

查询将显示输出,如下所示 -

查询输出

注意- 我们可以用 RESTful API 调用替换 Students.json 来检索学生数据,甚至是 MySQL 或 MongoDB 等真实数据库。GraphQL 成为原始应用程序层的薄包装,以提高性能。

GraphQL - 解析器

Resolver 是为 GraphQL 查询生成响应的函数集合。简单来说,解析器充当 GraphQL 查询处理程序。GraphQL 模式中的每个解析器函数都接受四个位置参数,如下所示 -

fieldName:(root, args, context, info) => { result }

解析器函数的示例如下所示 -

//resolver function  with no parameters and returning string
greeting:() => {
   return "hello from  TutorialsPoint !!!"
}

//resolver function with no parameters and returning list
students:() => db.students.list()

//resolver function with arguments and returning object
studentById:(root,args,context,info) => {
   return db.students.get(args.id);
}

下面给出的是位置参数及其描述 -

先生。 参数和描述
1

包含从父字段上的解析器返回的结果的对象。

2

参数

一个对象,其参数传递到查询中的字段中。

3

语境

这是特定查询中所有解析器共享的对象。

4

信息

它包含有关查询执行状态的信息,包括字段名称、从根到字段的路径。

解析器结果格式

GraphQL 中的解析器可以返回不同类型的值,如下所示 -

先生。 参数和描述
1

空或未定义

这表明无法找到该对象

2

大批

仅当模式指示字段的结果应该是列表时这才有效

3

承诺

解析器经常执行异步操作,例如从数据库或后端 API 获取数据,因此它们可以返回 Promise

4

标量或对象

解析器还可以返回其他值

插图

让我们创建一个简单的应用程序来理解解析器。这将创建用于通过服务器中的 id 查询学生的模式。学生数据将存储在平面文件中,我们将使用名为notarealdb的节点模块来伪造数据库并从平面文件中读取。

以下是创建简单应用程序的分步过程 -

第 1 步 - 下载并安装项目所需的依赖项

创建一个名为resolver-app 的文件夹。从终端将目录更改为resolver-app 。稍后,按照“环境设置”一章中的步骤 3 到 5 进行操作。

第 2 步 - 创建架构

在项目文件夹resolver-app中添加schema.graphql文件并添加以下代码 -

type Query { 
   greeting:String
   students:[Student]
   studentById(id:ID!):Student 
}

type Student {
   id:ID!
   firstName:String
   lastName:String
   password:String
   collegeId:String
}

架构文件显示用户可以查询greeting、studentsstudentById。为了检索具有特定 ID 的学生,我们使用数据类型 ID!它显示了一个不可为空的唯一标识符字段。Students字段返回学生数组,greeting返回一个简单的字符串值

第 3 步 - 创建解析器

在项目文件夹中创建文件resolvers.js并添加以下代码 -

const db = require('./db')
const Query = {
   //resolver function for greeting
   greeting:() => {
      return "hello from  TutorialsPoint !!!"
   },
   
   //resolver function for students returns list
   students:() => db.students.list(),

   //resolver function for studentbyId
   studentById:(root,args,context,info) => {
      //args will contain parameter passed in query
      return db.students.get(args.id);
   }
}
module.exports = {Query}

这里,studentById接受三个参数。正如本章所讨论的,studentId可以从 args 中检索;root 将包含查询对象本身。要返回特定的学生,我们需要使用学生集合中的 id 参数调用 get 方法。

这里的greeting、students、studentById是处理查询的解析器。学生解析器函数从数据访问层返回学生列表。要访问模块外部的解析器函数,必须使用 module.exports 导出 Query 对象。

第 4 步 - 运行应用程序

创建一个 server.js 文件。请参阅环境设置一章中的步骤 8。在终端中执行命令 npm start。服务器将在 9000 端口上启动并运行。在这里,我们使用 GraphiQL 作为客户端来测试应用程序。

打开浏览器并输入网址http://localhost:9000/graphiql。在编辑器中输入以下查询 -

{  
   studentById(id:"S1001") {
      id
      firstName
      lastName
   }
}

上述查询的输出如下所示 -

{
   "data": {
      "studentById": {
         "id": "S1001",
         "firstName": "Mohtashim",
         "lastName": "Mohammad"
      }
   }
}

GraphQL - 查询

GraphQL 操作可以是读取操作,也可以是写入操作。GraphQL 查询用于读取或获取值,而突变用于写入或发布值。无论哪种情况,操作都是一个简单的字符串,GraphQL 服务器可以解析该字符串并使用特定格式的数据进行响应。通常用于移动和 Web 应用程序的流行响应格式是 JSON。

定义查询的语法如下 -

//syntax 1
query query_name{ someField }

//syntax 2
{ someField }

以下是查询的示例 -

//query with name myQuery
query myQuery{
   greeting
}

// query without any name
{
   greeting
}

从上面的例子可以清楚地看出,查询关键字是可选的。

GraphQL 查询有助于减少数据的过度获取。与 Restful API 不同,GraphQL 允许用户限制应从服务器获取的字段。这意味着更少的查询和更少的网络流量;这反过来又减少了响应时间。

图 1 - 使用自定义字段查询学生模型

在此示例中,我们将一组学生存储在 json 文件中。每个学生模型都有名字、姓氏和 ID 等字段,但没有全名。在这里,我们将讨论如何进行查询来检索所有学生的全名。为此,我们需要在两个模式解析器中创建 fullName 字段。

让我们看看如何使用以下步骤来完成此插图 -

第 1 步 - 下载并安装项目所需的依赖项

创建一个名为query-app的文件夹。从终端将目录更改为query-app 。稍后,按照“环境设置”一章中说明的步骤 3 到 5 进行操作。

第 2 步 - 创建架构

在项目文件夹 query-app 中添加schema.graphql文件并添加以下代码 -

type Query {
   greeting:String
   students:[Student]
   studentById(id:ID!):Student
}

type Student {
   id:ID!
   firstName:String
   lastName:String
   fullName:String 
}

请注意, students.json文件中没有fullName字段。但是,我们需要通过查询获取学生的全名。在本例中, fullName将是数据源不可用的自定义字段。

第 3 步 - 创建解析器

在项目文件夹中创建文件resolvers.js并添加以下代码 -

const db = require('./db')
const Query = {
   //resolver function for greeting
   greeting:() => {
      return "hello from  TutorialsPoint !!!"
   },
   
   //resolver function for students returns list
   students:() => db.students.list(),

   //resolver function for studentbyId
   studentById:(root,args,context,info) => {
      //args will contain parameter passed in query
      return db.students.get(args.id);
   }
}

//for each single student object returned,resolver is invoked

const Student = {
   fullName:(root,args,context,info) => {
      return root.firstName+":"+root.lastName
   }
}

module.exports = {Query,Student}

第 4 步 - 运行应用程序

创建一个server.js文件。请参阅环境设置一章中的步骤 8。在终端中执行命令 npm start。服务器将在 9000 端口上启动并运行。在这里,我们使用 GraphiQL 作为客户端来测试应用程序。

打开浏览器并输入 URL http://localhost:9000/graphiql。在编辑器中输入以下查询 -

{
   students{
      id
      fullName
   }
}

查询的响应如下 -

{
   "data": {
      "students": [
         {
            "id": "S1001",
            "fullName": "Mohtashim:Mohammad"
         },
         
         {
            "id": "S1002",
            "fullName": "Kannan:Sudhakaran"
         },
         
         {
            "id": "S1003",
            "fullName": "Kiran:Panigrahi"
         }
      ]
   }
}

创建一个server.js并添加以下代码 -

const bodyParser = require('body-parser');
const cors = require('cors');
const express = require('express');

const db = require('./db');
const port = 9000;
const app = express();

//loading type definitions from schema file
const fs = require('fs')
const typeDefs = fs.readFileSync('./schema.graphql',{encoding:'utf-8'})

//loading resolvers
const resolvers = require('./resolvers')

//binding schema and resolver
const {makeExecutableSchema} = require('graphql-tools')
const schema = makeExecutableSchema({typeDefs, resolvers})

//enabling cross domain calls and form post
app.use(cors(), bodyParser.json());

//enabling routes
const  {graphiqlExpress,graphqlExpress} = require('apollo-server-express')
app.use('/graphql',graphqlExpress({schema}))
app.use('/graphiql',graphiqlExpress({endpointURL:'/graphql'}))

//registering port
app.listen(port, () => console.info(`Server started on port ${port}`));

在终端中执行命令 npm start。服务器将在 9000 端口上启动并运行。在这里,我们使用 GraphiQL 作为客户端来测试应用程序。

打开浏览器并输入 URL http://localhost:9000/graphiql。在编辑器中输入以下查询 -

{
   students{
      id
      fullName
   }
}

查询的响应如下 -

{
   "data": {
      "students": [
         {
            "id": "S1001",
            "fullName": "Mohtashim:Mohammad"
         },
         {
            "id": "S1002",
            "fullName": "Kannan:Sudhakaran"
         },
         {
            "id": "S1003",
            "fullName": "Kiran:Panigrahi"
         }
      ]
   }
}

图 2 - 嵌套查询

让我们创建一个嵌套查询来获取学生详细信息及其大学详细信息。我们将使用相同的项目文件夹。

第 1 步 - 编辑架构

模式文件已经具有学生字段。让我们添加一所野外学院并定义其类型。

type College {
   id:ID!
   name:String
   location:String
   rating:Float
}

type Student {
   id:ID!
   firstName:String
   lastName:String
   fullName:String
   college:College
}

步骤 2 - 修改resolver.js

我们需要添加一个大学解析器函数,如下所示。将为每个返回的学生对象执行大学解析器函数。在这种情况下,解析器的根参数将包含Student

const Student = {
   fullName:(root,args,context,info) => {
      return root.firstName+":"+root.lastName
   },
   college:(root) => {
      return db.colleges.get(root.collegeId);
   }
}
module.exports = {Query,Student}

解析器通过调用 College 集合的 get 方法并传递 CollegeId 返回每个学生的College我们通过CollegeId建立 Student 和 College 之间的关联关系。

第 3 步 - 测试应用程序

打开终端窗口并导航到项目文件夹。键入命令 -npm start。启动浏览器并输入 URL http://localhost:9000/graphiql

在 GraphiQL 窗口中输入以下查询 -

{
   students{
      id
      firstName
      college {
         id
         name
         location
         rating
      }
   }
}

查询的响应如下 -

{
   "data": {
      "students": [
         {
            "id": "S1001",
            "firstName": "Mohtashim",
            "college": {
               "id": "col-102",
               "name": "CUSAT",
               "location": "Kerala",
               "rating": 4.5
            }
         },
         
         {
            "id": "S1002",
            "firstName": "Kannan",
            "college": {
               "id": "col-101",
               "name": "AMU",
               "location": "Uttar Pradesh",
               "rating": 5
            }
         },
         
         {
            "id": "S1003",
            "firstName": "Kiran",
            "college": {
               "id": "col-101",
               "name": "AMU",
               "location": "Uttar Pradesh",
               "rating": 5
            }
         }
      ]
   }
}

什么是查询变量?

如果查询需要传递一些动态值,则使用变量表示这些动态值。因此,客户端应用程序可以重用该查询。

插图

让我们创建一个简单的应用程序来理解查询变量。

第 1 步 - 编辑架构文件

添加一个sayHello字段,它接受一个字符串参数并返回一个字符串。名称值在客户端应用程序中将是动态的。

type Query {
   sayHello(name:String!):String
}

步骤 2 - 编辑resolver.js 文件

添加一个sayHello解析器,其参数如下 -

sayHello:(root,args,context,info) => `Hi ${args.name} GraphQL server says Hello to you!!`

步骤 3 - 在 GraphiQL 中声明查询变量

变量用 $ 后跟变量名称来声明。例如:$myname_Variable。

一旦声明 $myname_Variable,它就必须与命名查询语法一起使用。查询 myQuery 获取字符串值并将其传递给 sayHello,如下所示 -

query myQuery($myname_Variable:String!) {
   sayHello(name:$myname_Variable)
}

在 GraphiQL 客户端的查询变量部分将 $myname_Variable 的值设置为 JSON 对象。

{
   "myname_Variable": "Mohtashim"
}

上述代码的输出如下 -

{
   "data": {
      "sayHello": "Hi Mohtashim GraphQL server says Hello to you!!"
   }
}
查询变量GraphiQL

如何将查询变量与枚举一起使用

让我们看看当字段参数是枚举类型时如何使用查询变量。

步骤 1 - 编辑 schema.graphql 文件

enum ColorType {
   RED
   BLUE
   GREEN
}

type Query {
   setFavouriteColor(color:ColorType):String
}

setFavouriteColor函数将枚举作为输入并返回一个字符串值。

步骤 2 - 编辑resolvers.js 文件

解析器函数setFavouriteColor采用rootargs。运行时传递给函数的枚举值可以通过 args 参数访问。

setFavouriteColor:(root,args) => {
   return  "Your Fav Color is :"+args.color;
}

步骤 3 - 在 GraphiQL 中声明查询变量

该查询名为query_to_setColor,它采用 ColorType 的名称 color_variable 的变量。该变量被传递给方法 setFavouriteColor。

query query_to_setColor($color_variable:ColorType) {
   setFavouriteColor(color:$color_variable)
}

在 GraphiQL 的查询变量部分中,输入以下代码 -

{
   "color_variable":"RED"
}

响应如下所示 -

{
   "data": {
      "setFavouriteColor": "Your Fav Color is: RED"
   }
}
声明查询变量

GraphQL - 突变

在本章中,我们将学习 GraphQL 中的变异查询。

突变查询修改数据存储中的数据并返回一个值。它可用于插入、更新或删除数据。突变被定义为模式的一部分。

突变查询的语法如下 -

mutation{
   someEditOperation(dataField:"valueOfField"):returnType
}

插图

让我们了解如何使用突变查询将新的学生记录添加到数据存储中。

第 1 步 - 下载并安装项目所需的依赖项

创建一个名为mutation-app 的项目文件夹。从终端将目录更改为mutation-app。请按照“环境设置”一章中说明的步骤 3 至 5 进行操作。

步骤 2 - 创建 schema.graphql 文件

在项目文件夹mutation-app中添加schema.graphql文件并添加以下代码 -

type Query {
   greeting:String
}

type Mutation {
   createStudent(collegeId:ID,firstName:String,lastName:String):String
}

请注意,函数 createStudent 返回 String 类型。这是创建学生后生成的唯一标识符 (ID)。

步骤 3 - 创建一个resolver.js 文件

在项目文件夹中创建文件resolvers.js并添加以下代码 -

const db = require('./db')
const Mutation = {
   createStudent:(root,args,context,info) => {
      return db.students.create({collegeId:args.collegeId,
      firstName:args.firstName,
      lastName:args.lastName})
   }
}
const Query = {
   greeting:() => "hello"
}

module.exports = {Query,Mutation}

突变函数指向数据存储中的学生集合。要添加新学生,请调用学生集合中的 create 方法。args对象将包含在查询中传递的参数。Students集合的 create 方法将返回新创建的学生对象的 id。

第 4 步 - 运行应用程序

创建一个server.js文件。请参阅“环境设置”一章中的步骤 8。在终端中执行命令 npm start。服务器将在 9000 端口上启动并运行。在这里,我们使用 GraphiQL 作为客户端来测试应用程序。

下一步是打开浏览器并输入 URL  http://localhost:9000/graphiql。在编辑器中输入以下查询 -

//college Id should be matched with data from colleges.json for easy retrieval

mutation {
   createStudent(collegeId:"col-2",firstName:"Tim",lastName:"George")
}

上面的查询将在 Student.json 文件中创建一个学生对象。查询将返回一个唯一标识符。查询的响应如下所示 -

{
   "data": {
      "createStudent": "SkQtxYBUm"
   }
}

要验证学生对象是否已创建,我们可以使用studentById 查询。您还可以从数据文件夹中打开 Students.json 文件来验证 ID。

要使用 StudentById 查询,请编辑schema.graphql,如下所示 -

type Query {
   studentById(id:ID!):Student
}

type Student {
   id:ID!
   firstName:String
   lastName:String
   collegeId:String
}

编辑resolver.js文件,如下所示 -

const db = require('./db')
const Query = {
   studentById:(root,args,context,info) => {
      return db.students.get(args.id);
   }
}

const Mutation = {
   createStudent:(root,args,context,info) => {
      return db.students.create({collegeId:args.collegeId,
      firstName:args.firstName,
      lastName:args.lastName})
   }
}

module.exports = {Query,Mutation}

下面给出的是通过从突变查询返回的唯一 id 获取学生的查询 -

{
    studentById(id:"SkQtxYBUm") {
    id
    firstName
    lastName
  }
}

服务器的响应如下 -

{
   "data": {
      "studentById": {
         "id": "SkQtxYBUm",
         "firstName": "Tim",
         "lastName":"George"
      }
   }
}

返回变异中的对象

最佳实践是返回突变中的对象。例如,客户端应用程序想要获取学生和大学的详细信息。在这种情况下,我们可以创建一个返回包含学生及其大学详细信息的对象的查询,而不是发出两个不同的请求。

第 1 步 - 编辑架构文件

添加一个名为addStudent的新方法,该方法返回schema.graphql突变类型的对象 。

让我们学习如何通过学生详细信息访问大学详细信息。在架构文件中添加大学类型。

type Mutation {
   addStudent_returns_object(collegeId:ID,firstName:String,lastName:String):Student

   createStudent(collegeId:ID,firstName:String,lastName:String):String
}

type College {
   id:ID!
   name:String
   location:String
   rating:Float
}

type Student {
   id:ID!
   firstName:String
   lastName:String
   college:College
}

步骤 2 - 更新resolvers.js 文件

更新项目文件夹中的文件 resolvers.js 并添加以下代码 -

const Mutation = {
   createStudent:(root,args,context,info) => {

      return db.students.create({
         collegeId:args.collegeId,
         firstName:args.firstName,
         lastName:args.lastName
      })
   },
   
   // new resolver function
   addStudent_returns_object:(root,args,context,info) => {
      const id = db.students.create({
         collegeId:args.collegeId,
         firstName:args.firstName,
         lastName:args.lastName
      })

      return db.students.get(id)
   }
}

//for each single student object returned,resolver is invoked
const Student = {
   college:(root) => {
      return db.colleges.get(root.collegeId);
   }
}

module.exports = {Query,Student,Mutation}

步骤 3 - 启动服务器并在 GraphiQL 中输入请求查询

接下来,我们将使用以下代码启动服务器并在 GraphiQL 中请求查询 -

mutation {
   addStudent_returns_object(collegeId:"col-101",firstName:"Susan",lastName:"George") {
      id
      firstName
      college{
         id
         name
      }
   }
}

上面的查询添加了一个新学生并检索学生对象和大学对象。这可以节省与服务器的往返次数。

响应如下 -

{
   "data": {
      "addStudent_returns_object": {
         "id": "rklUl08IX",
         "firstName": "Susan",
         "college": {
            "id": "col-101",
            "name": "AMU"
         }
      }
   }
}

GraphQL - 验证

添加或修改数据时,验证用户输入非常重要。例如,我们可能需要确保某个字段的值始终不为空。我们可以用!GraphQL 中的(不可为空)类型标记来执行此类验证。

使用!的语法 类型标记如下 -

type TypeName {
   field1:String!,
   field2:String!,
   field3:Int!
}

上述语法确保所有字段都不为空。

如果我们想要实现其他规则,例如检查字符串的长度或检查数字是否在给定范围内,我们可以定义自定义验证器。自定义验证逻辑将成为解析器函数的一部分。让我们通过一个例子来理解这一点。

插图 - 实现自定义验证器

让我们创建一个带有基本验证的注册表单。该表格将包含电子邮件、名字和密码字段。

第 1 步 - 下载并安装项目所需的依赖项

创建一个名为 validation-app的文件夹。从终端将目录更改为validation-app。请按照“环境设置”一章中说明的步骤 3 至 5 进行操作。

第 2 步 - 创建架构

在项目文件夹 validation-app中添加schema.graphql文件 并添加以下代码 -

type Query {
   greeting:String
}

type Mutation {
   signUp(input:SignUpInput):String
}

input SignUpInput {
   email:String!,
   password:String!,
   firstName:String!
}

注意- 我们可以使用输入类型 SignUpInput 来减少 SignUp 函数中的参数数量。因此,signUp 函数仅采用一个 SignUpInput 类型的参数。

第 3 步 - 创建解析器

在项目文件夹中创建文件 resolvers.js 并添加以下代码 -

const Query = {
   greeting:() => "Hello"
}

const Mutation ={
   signUp:(root,args,context,info) => {

      const {email,firstName,password} = args.input;

      const emailExpression = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
      
      const isValidEmail =  emailExpression.test(String(email).toLowerCase())
      if(!isValidEmail)
      throw new Error("email not in proper format")

      if(firstName.length > 15)
      throw new Error("firstName should be less than 15 characters")

      if(password.length < 8 )
      throw new Error("password should be minimum 8 charact