如何使用 Nest.js 构建 GraphQL API?
在本文中,您将学习如何利用 NestJS 的简洁架构和类型安全优势来构建 GraphQL API。
NestJS 是一个 NodeJS 框架,通过提供类型安全和严格的架构模式,帮助用户创建可扩展的企业级后端应用程序。
GraphQL是一种用于应用程序编程接口(API)的查询语言,因其速度快、稳定性好而被广泛用作 REST 的替代语言。GraphQL 的这一特性解决了 REST 带来的过度获取和获取不足的问题。
Contents
设置开发环境
如果系统中没有全局安装 Nest.js CLI,请运行下面的命令进行安装:
npm i -g @nestjs/cli
Nest.js CLI 包含多种命令,可帮助您生成模板代码,从而简化开发过程。
接下来,运行下面的命令创建项目并生成必要的启动文件:
nest new graphql-api
然后,运行下面的命令 cd 进入项目目录:
cd graphql-api
接下来,运行下面的命令安装所需的依赖项:
npm i @nestjs/graphql @nestjs/apollo graphql apollo-server-express
上面安装的依赖项包括
nest/graphql
nestjs/apollo
graphql
apollo-server-express
接下来,运行以下命令安装 TypeORM 软件包和 sqlite 数据库驱动程序:
npm install @nestjs/typeorm typeorm sqlite3
您需要安装上述软件包才能连接、读取和写入数据库。
在 Nest.js 中配置 GraphQL
Nest.js 提供两种创建 GraphQL API 的方法:代码优先法和模式优先法。
代码优先方法的特点是使用TypeScript 装饰器和类来生成相应的 GraphQL 模式。
模式优先方法的特点是使用 GraphQL 模式定义语言 (SDL) 文件,而 Nest.js 则使用基于 GraphQL 模式的类或接口生成 TypeScript 定义。
本教程的特色是使用代码优先方法,以避免在不同语言之间进行上下文切换。
要使用代码优先方法,请导航到应用程序的入口文件app.
module
.ts
,然后从@nestjs/graphql
导入GraphQLModule
。就像这样
import { GraphQLModule } from '@nestjs/graphql';
然后,在导入
数组中调用GraphQLModule
的forRoot
方法。该方法将一个配置对象作为参数。配置选项通过底层 GraphQL 驱动程序传递(本教程使用 Apollo 驱动程序)。将配置对象中的autoSchemaFile
属性设置为schema.gql
。就像这样
//app.module.ts
GraphQLModule.forRoot({ autoSchemaFile: 'schema.gql' }),
autoSchemaFile
属性是存储自动生成模式的文件路径。
连接数据库
要将应用程序连接到数据库,请浏览app.module.ts
文件并从@nestjs/typeorm
导入TypeOrmModule
。
就像这样
import { TypeOrmModule } from '@nestjs/typeorm';
接下来,将下面的代码块添加到导入
数组中:
TypeOrmModule.forRoot({
type: 'sqlite',
database: ':memory:',
entities: ['dist/**/*.entity{.ts,.js}'],
synchronize: true,
}),
上面的代码块在应用程序和 Sqlite 数据库之间创建了一个数据库连接,并在应用程序的所有模块中共享该连接。
生成资源
接下来,运行下面的命令创建一个模块来组织代码:
nest g module blog
上述命令将为您的应用程序生成一个博客模块。
接下来,运行下面的命令创建服务:
nest g service blog
上述代码块将在应用程序中生成并注册一个服务。该服务将包含并处理应用程序的所有业务逻辑。
接下来,运行下面的命令创建一个解析器:
nest g resolver
上面的代码块会在应用程序中生成并注册一个解析器。解析器指定将 GraphQL 查询和突变转化为数据的指令,并同步或异步返回模式中指定的数据形状。
创建实体
实体是一个字段集合,用于指定数据在数据库中的存储方式。
要创建实体,请在模块文件夹(blog)中,按照.entity.ts
惯例创建一个实体文件,例如blog.
entity.ts
。
在实体文件中,从typeorm
中导入列
、实体
和PrimaryGeneratedColumn
。就像这样
import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm';
Column
装饰器将其标注的字段添加为数据库中的列。实体
装饰器将其注释的类标记为实体。PrimaryGeneratedColumn
装饰器将其注释的属性标记为表的唯一标识符并自动生成。
接下来,创建并导出一个类Blog
。然后,用Entity
装饰器注释该类,将其标记为实体。就像这样
@Entity()
export class Blog {}
由于本教程以博客 API 为特色,因此请在类中添加 id、title、body、author 和 date 属性。然后,用Column
装饰器注解除id
属性以外的每个属性。使用PrimaryGeneratedColumn
装饰器注解id
属性。
就像这样
import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm';
@Entity()
export class Blog {
@PrimaryGeneratedColumn()
id: number;
@Column()
title: string;
@Column()
body: string;
@Column()
author: string;
@Column()
date: string;
}
创建对象类型
对象类型是 GraphQL 模式定义,代表客户端需要与之交互的对象。例如,如果要构建博客 API,就需要用 TypeScript 类定义博客模式/实体,并用适当的装饰器注释每个字段。
接下来,在实体文件中,从@nestjs/graphql
导入Int
类型以及Field
和ObjectType
装饰器。像这样
import { Field, Int, ObjectType } from '@nestjs/graphql';
字段
装饰器将特定类属性标记为 GraphQL 字段。只有使用该装饰器注释的属性才会在模式中定义。它接受一个可选的返回类型函数和一个配置对象作为参数。
需要使用Int
类型来消除 TypeScript 和 GraphQL 类型系统之间潜在的歧义。只有具有数字值的属性才需要使用 Int 类型,因为它们既可以是整数,也可以是浮点数。
ObjectType
装饰器将一个类标记为 GraphQL 类型。
接下来,用ObjectType
装饰器注释 Blog 类,将其标记为 GraphQL 类型。就像这样
@ObjectType()
export class Blog {}
最后,在类中指定您的类型所需的属性,并用Field
装饰器为每个属性注释。例如
//blog.entity.ts
import { Field, Int, ObjectType } from '@nestjs/graphql';
import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm';
@Entity()
@ObjectType()
export class Blog {
@PrimaryGeneratedColumn()
@Field((type) => Int)
id: number;
@Column()
@Field()
title: string;
@Column()
@Field()
body: string;
@Column()
@Field()
author: string;
@Column()
@Field()
date: string;
}
完成后的对象类型应与上面的代码块相似。
请注意,上述代码块中数字
类型的id
已明确指定为整数,以避免 TypeScript 和 GraphQL 类型系统之间的歧义。
最后,将实体注入应用程序,在应用程序和数据库之间创建一个访问层。
为此,请导航至blog.module.ts
文件,从@nestjs/typeorm
导入TypeOrmModule
,然后导入博客实体。
然后,将下面的代码块添加到模块装饰器中:
imports: [TypeOrmModule.forFeature([Blog])],
上面的代码块创建了一个存储库,作为应用程序和数据库之间的访问层。
创建服务
导航至先前生成的服务文件blog.service.ts
,然后从@nestjs/typeorm
中导入InjectRepository
和对象类型。
就像这样
import { InjectRepository } from '@nestjs/typeorm';
import { Blog } from './blog.entity';
接下来,在BlogService
类中添加下面的代码块,以注入存储库:
constructor(
@InjectRepository(Blog)
private blogRepository: Repository<Blog>,
) {}
接下来,在服务类中添加下面的代码块,以实现检索数据库中所有博客的逻辑:
async getAll(): Promise<Blog[]> {
return await this.blogRepository.find();
}
接下来,你需要实现在数据库中添加新博客的逻辑,但首先,你需要创建一个输入类型。
创建输入类型
要为你的突变创建一个可接收复杂对象的输入类型,请在你的博客
文件夹中创建一个dtos
文件夹。然后,在dtos
文件夹中创建一个新文件create-blog.input.ts
。
在create-blog.input.ts
文件中,导入Field
和InputType
装饰器。就像这样
import { Field, InputType } from '@nestjs/graphql';
InputType
装饰器将一个类标记为 GraphQL 输入类型。
接下来,创建并导出一个类BlogInput
。然后,用InputType
装饰器注解该类。就像这样
@InputType()
export class BlogInput {}
最后,添加所有所需的博客属性,并使用Field
装饰器对其进行注释。
就像这样
//create-blog.input.ts
import { Field, InputType } from '@nestjs/graphql';
@InputType()
export class BlogInput {
@Field()
id: number;
@Field()
title: string;
@Field()
body: string;
@Field()
author: string;
@Field()
date: string;
}
完成后的输入类型应与上面的代码块相似。
接下来,将输入类型导入服务文件,并在服务类中添加以下代码块,以实现向数据库添加博客的逻辑:
async createBlog(blogInput: BlogInput): Promise<Blog> {
const newBlog = this.blogRepository.create(blogInput);
return this.blogRepository.save(newBlog);
}
输入类型的应用方式与常规 Nest.js 数据传输对象的应用方式相同,是对输入数据的一种验证机制。与 dtos 一样,你也可以使用类验证器
软件包对数据执行额外的验证。
创建解析器
导航至之前生成的 Resolver 文件blog.resolver.ts
,然后导入实体、输入类型和服务。就像这样
import { Blog } from './blog.entity';
import { BlogService } from './blog.service';
import { BlogInput } from './dto/create-blog.input';
接下来,在 Resolver 类中添加下面的代码块,将服务注入到解析器中:
constructor(private blogService: BlogService) {}
接下来,从@nestjs/graphql
中导入Args
、Mutation
和Query
装饰器。
就像这样
import { Args, Mutation, Query } from '@nestjs/graphql';
Args
装饰器从底层平台提取参数对象,并用所有参数或单个指定参数的值填充装饰后的参数。
突变
装饰器将路由指定为突变。
Query
装饰器将路由指定为查询。
接下来,将下面的函数传递给Resolver
装饰器,以指定对象类型:
(of) => Blog
然后,在 Resolver 类中添加下面的代码块,以解析getAll
方法。
@Query((type) => [Blog])
async getAllBlogs() {
return this.blogService.getAll();
}
最后,在 Resolver 类中添加以下代码块,以解析createBlog
方法。
@Mutation((returns) => Blog)
createBlog(@Args('blogInput') blogInput: BlogInput): Promise<Blog> {
return this.blogService.createBlog(blogInput);
}
注意:请务必为每个装饰器指定返回类型函数。
完成后的 Resolver 类应该与下面的代码块相似:
@Resolver((of) => Blog)
export class BlogResolver {
constructor(private blogService: BlogService) {}
@Query((type) => [Blog])
async getAllBlogs() {
return this.blogService.getAll();
}
@Mutation((returns) => Blog)
createBlog(@Args('blogInput') blogInput: BlogInput): Promise<Blog> {
return this.blogService.createBlog(blogInput);
}
}
使用 GraphQL Playground 测试您的应用程序接口
要使用默认提供的 GraphQL playground 测试应用程序,请运行以下命令并导航至http://localhost:3000/graphql。
npm start
接下来,你会看到一个游戏场地,在这里可以测试你的查询和突变,如下图所示。
要从数据库中检索所有博客,请运行下面的查询:
query getAll{
getAllBlogs {
id,
title,
body,
author,
date
}
}
下图就是一个例子:
要在数据库中添加博客,请运行下面的突变:
mutation createBlog {
createBlog (blogInput: {
id: 1,
title: "GraphQL is great",
body: "GraphQL APIs are faster than REST APIs",
author: "David Ekete",
date: "01-02-03"
}) {
id,
title,
body,
author,
date,
}
}
下图就是一个例子:
Back4app 和 GraphQL
与 REST 相比,GraphQL 具有很多优势,例如数据获取控制,因此 Back4app 平台提供了一个 GraphQL API,集成到平台上运行的解析应用程序中。
通过该集成,您可以编写简单的 GraphQL 查询和突变,检查服务器的后端健康状况,在数据库上执行创建、读取、更新、删除 (CRUD) 等操作。
例如,要使用 GraphQL API 检查后端服务器的健康状况,请运行下面的查询:
query Health {
health
}
上述查询将返回
{
"data": {
"health": true
}
}
要开始使用 GraphQL 功能,请导航到 Parse 面板,选择并点击你的应用程序。在应用程序的用户界面中,选择并单击核心,然后选择并单击 API 控制台,再选择 GraphQL 控制台。
按照上述说明操作将打开一个 GraphQL 控制台,您可以在此运行查询和突变,类似于您在开发过程中使用的 GraphQL playground。您可以在Back4app 文档中了解有关Back4app GraphQL 功能的更多信息。
结论
本文介绍了如何在 Nest.js 中创建 GraphQL API 并使用 GraphQL playground 测试 API。与 REST API 相比,GraphQL API 有几个优势。
但是,它们并不是每个应用程序的理想选择。在选择哪个适合您的应用之前,请务必考虑 GraphQL 和 REST 的优缺点。
无论您选择 GraphQL 还是 REST,您仍然需要部署您的应用程序。Back4app是一款基于开源技术的低代码 BaaS,通过使用可重复使用的模板消除重复性任务并管理大部分后端基础设施,从而简化后端构建流程。
Back4app 的部分功能包括实时数据库、云功能、本地 API 和 SDK、完整的用户管理系统等。
该平台还提供免费层级,让您无需付费即可探索该平台。
快乐建筑
常见问题
什麼是 GraphQL?
GraphQL 是一種用於 API 的開源資料查詢和操作語言,由 Facebook 於 2012 年創建。
什麼是 Nest.JS?
Nest.JS 是一個開源 Node.js 框架,用於建立高效、可擴展的伺服器端應用程式。
如何使用 Nest.JS 建立 GraphQL API?
– 在 Nest.js 中設定 GraphQL
– 連接到資料庫
– 產生資源
– 建立一個實體
– 建立物件類型
– 創建服務