理解API代码:高效REST API集成的综合指南
使用NestJS和Prisma构建REST API
NestJS 是一个著名的 Node.js 框架,它最近获得了很多开发人员的喜爱和关注。本文将教你如何使用 NestJS、Prisma、PostgreSQL 和 Swagger 构建后端 REST API。
引言
在本教程中,您将学习如何为一个名为“Median”(一个简单的Medium克隆版博客应用)构建后端REST API。您将从创建一个新的NestJS项目开始。然后,您将启动自己的PostgreSQL服务器,并利用Prisma与之建立连接。最后,您将构建REST API并使用Swagger进行文档记录。
您将使用的技术
您将使用以下工具来构建此应用程序:
- NestJS 作为后端框架
- Prisma 作为对象关系映射器 (ORM)
- PostgreSQL 作为数据库
- Swagger 作为 API 文档工具
- TypeScript 作为编程语言
先决条件
假定知识
这是一个适合初学者的教程。但假设您已具备以下基础:
- 具备 JavaScript 或 TypeScript 的基本知识(首选)
- 对 NestJS 有基础了解
注意:如果您不熟悉 NestJS,可以按照 NestJS 文档中的概述部分快速学习基础知识。
开发环境
为了学习本教程,您需要确保以下环境已就绪:
- …已安装Node.js。
- …已安装 Docker 或 PostgreSQL。
- …已安装 Prisma VSCode 扩展。(可选)
- …可以访问 Unix shell(如 Linux 和 macOS 中的终端/shell)来运行本系列中提供的命令。(可选)
注 1:安装Prisma VSCode扩展将提升您的编码体验,通过提供智能代码补全和语法高亮。
注 2:若您没有Unix shell环境(如Windows用户),您仍然可以跟随教程,但可能需要根据您的操作系统调整部分shell命令。
生成 NestJS 项目
首要步骤是安装 NestJS CLI(命令行接口)。在开发 NestJS 应用时,NestJS CLI 是一个不可或缺的工具。它集成了多种实用功能,能够助您轻松初始化、开发和维护 NestJS 应用。
接下来,利用 NestJS CLI 创建一个全新的空项目。只需在您期望存放项目的目录中执行以下命令:
npx @nestjs/cli new median
CLI将提示您为项目选择一个包管理器-选择npm。之后,您应该在当前目录中有一个新的NestJS项目。
在您喜欢的代码编辑器中打开项目(我们推荐VSCode)。您应该看到以下文件:
median
├── node_modules
├── src
│ ├── app.controller.spec.ts
│ ├── app.controller.ts
│ ├── app.module.ts
│ ├── app.service.ts
│ └── main.ts
├── test
│ ├── app.e2e-spec.ts
│ └── jest-e2e.json
├── README.md
├── nest-cli.json
├── package-lock.json
├── package.json
├── tsconfig.build.json
└── tsconfig.json
所处理的大部分代码都会存放在 src
目录中。NestJS CLI 已经为您预先创建了一些基础文件,其中几个尤为关键:
src/app.module.ts
:这是应用程序的根模块,承载着整个应用的配置与组件。src/app.controller.ts
:这是一个基础控制器,包含了一个简单的路由/
,该路由会返回一个“Hello World!”的提示信息。src/main.ts
:这是应用程序的启动入口,负责启动并运行 NestJS 应用。
要启动项目,您可以执行以下命令:
npm run start:dev
该命令将监视您的文件,每当您进行更改时自动重新编译和重新加载服务器。要验证服务器是否正在运行,请转到URL http://localhost:3000/
。您应该会看到一个空页面,其中包含消息'Hello World!'
。
注意:在学习本教程时,您应该保持服务器在后台运行。
创建 PostgreSQL 实例
本教程将指导您如何在计算机上通过 Docker 容器安装并运行 PostgreSQL,以作为 NestJS 应用的数据库。
注意:若您不打算使用 Docker,还可以选择在本地搭建 PostgreSQL 实例,或在 Heroku 上获取托管的 PostgreSQL 数据库服务。
首先,在项目的主文件夹中创建一个docker-compose.yml
文件:
touch docker-compose.yml
这个 docker-compose.yml
文件是一个配置文件,它将包含运行docker容器的规范,其中包含PostgreSQL设置。在文件中创建以下配置:
# docker-compose.yml
version: '3.8'
services:
postgres:
image: postgres:13.5
restart: always
environment:
- POSTGRES_USER=myuser
- POSTGRES_PASSWORD=mypassword
volumes:
- postgres:/var/lib/postgresql/data
ports:
- '5432:5432'
volumes:
postgres:
volumes: postgres:
关于此配置,需要了解以下几点:
image
选项定义了要使用的 Docker 镜像。在这里,您使用的是postgres
镜像的 13.5 版本。environment
选项指定了在容器初始化期间传递给容器的环境变量。您可以在此处定义容器将使用的配置选项和机密信息,例如用户名和密码。volumes
选项用于在主机文件系统中持久化数据。ports
选项将主机上的端口映射到容器中的端口。格式遵循“主机端口:容器端口”的约定。在本例中,您将主机上的 5432 端口映射到postgres
容器的 5432 端口。5432 是 PostgreSQL 传统上使用的端口。
请确保您的机器上没有其他服务正在使用 5432 端口。要启动 postgres
容器,请打开一个新的终端窗口,并在项目的主文件夹中运行以下命令:
docker-compose up
如果一切正常,新的终端窗口应显示数据库系统已准备好接受连接的日志。您应该会在终端窗口中看到类似于以下内容的日志:
...
postgres_1 | 2022-03-05 12:47:02.410 UTC [1] LOG: listening on IPv4 address "0.0.0.0", port 5432
postgres_1 | 2022-03-05 12:47:02.410 UTC [1] LOG: listening on IPv6 address "::", port 5432
postgres_1 | 2022-03-05 12:47:02.411 UTC [1] LOG: listening on Unix socket "/var/run/postgresql/.s.PGSQL.5432"
postgres_1 | 2022-03-05 12:47:02.419 UTC [1] LOG: database system is ready to accept connections
恭喜 🎉。您现在已成功拥有了自己的 PostgreSQL 数据库!
注意:若您关闭终端窗口,容器也会随之停止。为避免这种情况,您可以在命令末尾添加
-d
选项,例如:docker-compose up -d
。这样容器就会在后台持续运行,不会因终端关闭而中断。
设置 Prisma
既然数据库已经准备就绪,接下来就该配置 Prisma 了!
初始化 Prisma
首先,您需要将 Prisma CLI 安装为项目的开发依赖项。Prisma CLI 提供了多种命令,方便您与项目进行交互。
npm install -D prisma
您可以通过运行以下命令在项目中初始化 Prisma:
npx prisma init
这将创建一个新的 Prisma
目录和一个 schema.prisma
文件。这是包含数据库模式的主配置文件。这个命令也会在你的项目中创建一个.env
文件。
设置环境变量
在 .env
文件中,您会发现一个名为 DATABASE_URL
的环境变量,它包含一个虚拟的连接字符串。您需要将这个虚拟的连接字符串替换为您 PostgreSQL 实例的实际连接字符串。
// .env
DATABASE_URL="postgres://myuser:mypassword@localhost:5432/median-db"
注意:如果您没有使用 docker(如上一节所示)来创建 PostgreSQL 数据库,则您的连接字符串将与上面显示的连接字符串不同。关于PostgreSQL 的连接字符串格式,您可以在 Prisma 官方文档中查阅到。
了解 Prisma 架构
如果打开 prisma/schema.prisma
,您应该看到以下默认模式:
// prisma/schema.prisma
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
schema.prisma
文件是使用 Prisma Schema 语言编写的,这是 Prisma 用于定义数据库架构的专有语言。该文件主要包含以下三个核心组成部分:
- 数据源:指定数据库连接。上面的配置意味着您的数据库提供者是PostgreSQL,并且数据库连接字符串在
DATABASE_URL
环境变量中可用。 - 生成器:表示您要生成 Prisma Client,这是数据库的类型安全查询生成器。它用于向数据库发送查询。
- 数据模型:定义您的数据库模型。每个模型都将映射到底层数据库中的表。现在,您的 schema 中没有模型,您将在下一节中探索这部分。
注意:有关 Prisma 架构的更多信息,请查看 Prisma 文档。
对数据进行建模
现在,是时候为您的应用程序定义数据模型了。在本教程中,我们仅需要一个名为 Article
的模型,用以代表博客上的每一篇文章。
请打开 prisma/schema.prisma
文件,并向其中添加一个全新的 Article
模型:
// prisma/schema.prisma
model Article {
id Int @id @default(autoincrement())
title String @unique
description String?
body String
published Boolean @default(false)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
您已经成功创建了一个功能完备的 Article
模型。该模型包含多个字段,如 id
、title
等,每个字段都具备名称、类型以及可选的特殊属性(例如 @id
、@unique
等)。通过在字段类型后添加 ?
,您可以轻松地将某个字段设置为可选。
id
字段被赋予了@id
的特殊属性,这标志着它是该模型的主键。而 @default(autoincrement())
属性则确保每当有新记录创建时,该字段都会自动递增并分配一个唯一的值。
published
字段是指示文章是已发布还是处于草稿模式的标志。@default(false)
属性表示默认情况下该字段应设置为false
。
两个DateTime
字段 createdAt
和 updatedAt
将跟踪文章的创建时间和上次更新时间。@updatedAt
属性会自动将该字段更新为当前时间戳,无论何时文章被修改,这个字段都会与当前时间同步更新。
迁移数据库
定义 Prisma 架构后,您将运行迁移以在数据库中创建实际表。要生成并执行您的第一个迁移,请在终端中运行以下命令:
npx prisma migrate dev --name "init"
此命令将执行三项操作:
- 保存迁移:Prisma Migrate会首先捕捉您数据库模式的当前快照,并据此分析出将您的数据库模式更新到最新状态所需的SQL命令。随后,Prisma会将这些SQL命令封装进迁移文件中,并将这些文件保存到新创建的
prisma/migrations
文件夹中。这个过程为您的数据库模式变更提供了详细的记录和步骤。 - 执行迁移:一旦迁移文件被创建,Prisma Migrate就能够执行这些文件中的SQL命令,从而在数据库中实际创建或更新底层表结构。这个过程确保了您的数据库模式与Prisma模式文件中的定义保持一致。
- 生成Prisma客户端:在迁移执行完毕后,Prisma会根据您最新的数据库模式自动生成Prisma客户端。如果您尚未安装客户端库,Prisma CLI也会为您自动安装。您可以在
package.json
文件的dependencies
部分找到名为@prisma/client
的包。Prisma Client是一个功能强大的TypeScript查询生成器,它根据您的Prisma模式文件自动生成与数据库交互的代码。这个客户端是为您的特定Prisma模式量身定制的,它将大大简化您向数据库发送查询的过程。
注意:您可以在 Prisma 文档中了解有关 Prisma Migrate 的更多信息。
如果成功完成,您应该会看到如下消息:
The following migration(s) have been created and applied from new schema changes:
migrations/
└─ 20220528101323_init/
└─ migration.sql
Your database is now in sync with your schema.
...
✔ Generated Prisma Client (3.14.0 | library) to ./node_modules/@prisma/client in 31ms
检查生成的迁移文件,了解 Prisma Migrate 在幕后的作用:
-- prisma/migrations/20220528101323_init/migration.sql
-- CreateTable
CREATE TABLE "Article" (
"id" SERIAL NOT NULL,
"title" TEXT NOT NULL,
"description" TEXT,
"body" TEXT NOT NULL,
"published" BOOLEAN NOT NULL DEFAULT false,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "Article_pkey" PRIMARY KEY ("id")
);
-- CreateIndex
CREATE UNIQUE INDEX "Article_title_key" ON "Article"("title");
注意:迁移文件的名称将略有不同。
这是在PostgreSQL数据库中创建 Article
表所需的SQL。它是由Prisma根据您的Prisma模式自动生成和执行的。
种子数据库
目前,数据库为空。因此,您将创建一个种子脚本,该脚本将使用一些虚拟数据填充数据库。
首先,创建一个名为prisma/seed.ts
的文件,这个文件将包含初始化数据库种子所需的模拟数据和查询。
touch prisma/seed.ts
然后,在seed文件中添加以下代码:
// prisma/seed.ts
import { PrismaClient } from '@prisma/client';
// initialize Prisma Client
const prisma = new PrismaClient();
async function main() {
// create two dummy articles
const post1 = await prisma.article.upsert({
where: { title: 'Prisma Adds Support for MongoDB' },
update: {},
create: {
title: 'Prisma Adds Support for MongoDB',
body: 'Support for MongoDB has been one of the most requested features since the initial release of...',
description:
"We are excited to share that today's Prisma ORM release adds stable support for MongoDB!",
published: false,
},
});
const post2 = await prisma.article.upsert({
where: { title: "What's new in Prisma? (Q1/22)" },
update: {},
create: {
title: "What's new in Prisma? (Q1/22)",
body: 'Our engineers have been working hard, issuing new releases with many improvements...',
description:
'Learn about everything in the Prisma ecosystem and community from January to March 2022.',
published: true,
},
});
console.log({ post1, post2 });
}
// execute the main function
main()
.catch((e) => {
console.error(e);
process.exit(1);
})
.finally(async () => {
// close Prisma Client at the end
await prisma.$disconnect();
});
在这个脚本中,首先需要初始化Prisma客户端。接着,使用prisma.upsert()
函数来创建两个项目。upsert
函数只会在没有文章符合where
条件时才创建一个新文章。您选择使用upsert
查询而不是create
查询,这是因为upsert
可以避免因意外尝试两次插入相同记录而产生的错误。
您需要指定Prisma在执行种子命令时运行哪个脚本。您可以通过在 package.json
文件的末尾添加 prisma.seed
键来实现这一点:
// package.json
// ...
"scripts": {
// ...
},
"dependencies": {
// ...
},
"devDependencies": {
// ...
},
"jest": {
// ...
},
"prisma": {
"seed": "ts-node prisma/seed.ts"
}
seed
命令将执行您之前定义的 prisma/seed.ts
脚本。由于ts-node
已经作为开发依赖安装在您的package.json
中,这个命令应该能够自动运行。
请使用以下命令来执行数据库的种子数据初始化:
npx prisma db seed
您应该会看到以下输出:
Running seed command `ts-node prisma/seed.ts` ...
{
post1: {
id: 1,
title: 'Prisma Adds Support for MongoDB',
description: "We are excited to share that today's Prisma ORM release adds stable support for MongoDB!",
body: 'Support for MongoDB has been one of the most requested features since the initial release of...',
published: false,
createdAt: 2022-04-24T14:20:27.674Z,
updatedAt: 2022-04-24T14:20:27.674Z
},
post2: {
id: 2,
title: "What's new in Prisma? (Q1/22)",
description: 'Learn about everything in the Prisma ecosystem and community from January to March 2022.',
body: 'Our engineers have been working hard, issuing new releases with many improvements...',
published: true,
createdAt: 2022-04-24T14:20:27.705Z,
updatedAt: 2022-04-24T14:20:27.705Z
}
}
🌱 The seed command has been executed.
注意:您可以在 Prisma Docs 中了解有关种子设定的更多信息。
创建 Prisma 服务
在NestJS应用程序中,将Prisma Client API从应用逻辑中抽象出来是一个好习惯。为此,您将创建一个新的服务来封装Prisma Client。这个服务名为 PrismaService
,它负责创建一个 PrismaClient
实例并连接到您的数据库。
Nest CLI提供了一种方便的方法,可以直接从命令行界面(CLI)生成模块和服务。在终端中运行以下命令:
npx nest generate module prisma
npx nest generate service prisma
注 1:如有需要,请参考 NestJS 文档了解服务和模块的介绍。
注 2:在某些情况下,在服务器已经运行的情况下运行
nest generate
命令可能会导致NestJS抛出异常:Error: Cannot find module './app.controller'
。如果您遇到此错误,请从终端运行以下命令:rm -rf dist
并重新启动服务器。
这应该会生成一个新的带有./src/prisma
和 prisma.module.ts
文件的文件夹 prisma.service.ts
。服务文件应包含以下代码:
// src/prisma/prisma.service.ts
import { INestApplication, Injectable } from '@nestjs/common';
import { PrismaClient } from '@prisma/client';
@Injectable()
export class PrismaService extends PrismaClient {}
Prisma模块将负责创建 PrismaService
的单例实例,并允许在整个应用程序中共享服务。要做到这一点,你需要将 PrismaService
添加到 exports
文件中的 prisma.module.ts
数组中:
// src/prisma/prisma.module.ts
import { Module } from '@nestjs/common';
import { PrismaService } from './prisma.service';
@Module({
providers: [PrismaService],
exports: [PrismaService],
})
export class PrismaModule {}
现在,任何导入PrismaModule
的模块都可以访问PrismaService
,并将其注入到自己的组件/服务中。这是NestJS应用程序的常见模式。
完成后,您就完成了 Prisma 的设置!您现在可以开始构建 REST API。
设置 Swagger
Swagger 是一个基于 OpenAPI 规范来记录 API 的工具。Nest 提供了一个专门的 Swagger 模块,您很快就会用到它。
首先安装所需的依赖项:
npm install --save @nestjs/swagger swagger-ui-express
现在打开 main.ts
并使用 SwaggerModule
类初始化Swagger:
// src/main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
const config = new DocumentBuilder()
.setTitle('Median')
.setDescription('The Median API description')
.setVersion('0.1')
.build();
const document = SwaggerModule.createDocument(app, config);
SwaggerModule.setup('api', app, document);
await app.listen(3000);
}
bootstrap();
当应用程序运行时,打开浏览器并导航到 http://localhost:3000/api
. 您应该能够看到 Swagger UI 界面。
为Article模型实现 CRUD 操作
在本节中,您将为Article模型实现创建(Create)、读取(Read)、更新(Update)和删除(Delete)操作,以及任何相关的业务逻辑。
生成 REST 资源
在您能够实现REST API之前,您需要为Article模型生成REST资源。这可以通过Nest CLI快速完成。在终端中运行以下命令
npx nest generate resource
您将收到一些 CLI 提示。请相应地回答问题:
- 您想为这个资源使用什么名称(复数形式,例如,“users”)? articles
- 您使用的传输层是什么? REST API
- 您是否希望生成CRUD入口点? 是
现在您应该在src/articles目录下找到了所有REST端点的样板代码。在src/articles/articles.controller.ts文件中,您将看到不同路由(也称为路由处理器)的定义。处理每个请求的业务逻辑封装在src/articles/articles.service.ts文件中。目前,这个文件包含一些虚拟实现。
如果您再次打开 Swagger API 页面,您应该会看到如下内容:
SwaggerModule会搜索所有路由处理器上的@Body()、@Query()和@Param()装饰器,以生成这个API页面。
将PrismaClient添加到Articles模块
要在Articles模块内访问PrismaClient,您必须将PrismaModule作为一个导入添加。在ArticlesModule中添加以下导入:
// src/articles/articles.module.ts
import { Module } from '@nestjs/common';
import { ArticlesService } from './articles.service';
import { ArticlesController } from './articles.controller';
import { PrismaModule } from 'src/prisma/prisma.module';
@Module({
controllers: [ArticlesController],
providers: [ArticlesService],
imports: [PrismaModule],
})
export class ArticlesModule {}
您现在可以将 PrismaService
注入到 ArticlesService
中,并使用它来访问数据库。为此,您需要在articles.service.ts
文件中添加一个构造函数,如下所示:
// src/articles/articles.service.ts
import { Injectable } from '@nestjs/common';
import { CreateArticleDto } from './dto/create-article.dto';
import { UpdateArticleDto } from './dto/update-article.dto';
import { PrismaService } from 'src/prisma/prisma.service';
@Injectable()
export class ArticlesService {
constructor(private prisma: PrismaService) {}
// CRUD operations
}
定义 GET /articles 终端节点
该端点的控制器名为 findAll
。此端点将返回数据库中所有已发布的文章。findAll
控制器的实现如下所示:
// src/articles/articles.controller.ts
@Get()
findAll() {
return this.articlesService.findAll();
}
您需要更新 ArticlesService.findAll()
以返回数据库中所有已发布文章的数组:
// src/articles/articles.service.ts
@Injectable()
export class ArticlesService {
constructor(private prisma: PrismaService) {}
create(createArticleDto: CreateArticleDto) {
return 'This action adds a new article';
}
findAll() {
return `This action returns all articles`;
return this.prisma.article.findMany({ where: { published: true } });
}
findMany
查询将返回与 article
条件匹配的所有 where
记录。
您可以通过访问 http://localhost:3000/api 并点击 GET /articles 菜单来测试这个端点。点击“Try it out”,然后点击“Execute”按钮来查看结果。
注意:您也可以直接在浏览器中运行所有请求,或者通过REST客户端(如Postman)运行所有请求。Swagger还为每个请求生成curl命令,以防您希望在终端中运行HTTP请求。
定义 GET /articles/drafts 终端节点
您将定义一个新的路由来获取所有未发布的文章。NestJS 没有为这个端点自动生成控制器路由处理器,因此您需要自行编写它。
// src/articles/articles.module.ts
import { Module } from '@nestjs/common';
import { ArticlesService } from './articles.service';
import { ArticlesController } from './articles.controller';
import { PrismaModule } from 'src/prisma/prisma.module';
@Module({
controllers: [ArticlesController],
providers: [ArticlesService],
imports: [PrismaModule],
})
export class ArticlesModule {}
你的编辑器应该显示一个错误,提示没有名为articlesService.findDrafts()
的函数存在。要解决这个问题,请在 findDrafts
中实现 ArticlesService
方法:
// src/articles/articles.service.ts
@Injectable()
export class ArticlesService {
constructor(private prisma: PrismaService) {}
create(createArticleDto: CreateArticleDto) {
return 'This action adds a new article';
}
findDrafts() {
return this.prisma.article.findMany({ where: { published: false } });
}
// ...
}
GET /articles/drafts 端点现在应该已经在 Swagger API 页面中可用了。
注意:我建议您在完成实施后通过 Swagger API 页面测试每个终端节点。
定义 GET /articles/:id 终端节点
此端点的控制器路由处理器名为findOne
。它的实现如下所示:
// src/articles/articles.controller.ts
@Get(':id')
findOne(@Param('id') id: string) {
return this.articlesService.findOne(+id);
}
路由接受一个动态的id
参数,该参数被传递给findOne
控制器路由处理程序。由于Article
模型具有整数id
字段,因此需要使用id
运算符将+
参数转换为数字。
现在,更新 findOne
中的 ArticlesService
方法,以返回具有给定id的文章:
// src/articles/articles.service.ts
@Injectable()
export class ArticlesService {
constructor(private prisma: PrismaService) {}
create(createArticleDto: CreateArticleDto) {
return 'This action adds a new article';
}
findAll() {
return this.prisma.article.findMany({ where: { published: true } });
}
findOne(id: number) {
return `This action returns a #${id} article`;
return this.prisma.article.findUnique({ where: { id } });
}
}
请访问 http://localhost:3000/api,然后点击 GET /articles/{id} 下拉菜单。点击“Try it out”(试用),在 id 参数中输入一个有效的值,接着点击“Execute”(执行)来查看结果。
定义POST /articles终端节点
这是创建新文章的端点,此端点的控制器路由处理程序称为 create
。它的实现如下所示:
// src/articles/articles.controller.ts
@Post()
create(@Body() createArticleDto: CreateArticleDto) {
return this.articlesService.create(createArticleDto);
}
请注意,它在请求体中期望一个类型为CreateArticleDto
的参数。DTO(Data Transfer Object,数据传输对象)是一个定义数据如何通过网络发送的对象。目前,CreateArticleDto
是一个空类。您需要向其中添加属性,以定义请求正文的结构。复制再试一次分享
// src/articles/dto/create-article.dto.ts
import { ApiProperty } from '@nestjs/swagger';
export class CreateArticleDto {
@ApiProperty()
title: string;
@ApiProperty({ required: false })
description?: string;
@ApiProperty()
body: string;
@ApiProperty({ required: false, default: false })
published?: boolean = false;
}
需要使用 @ApiProperty
装饰器来使类属性对 SwaggerModule
可见。更多关于这方面的信息可以在NestJS文档中找到。
现在,您应该在Swagger API页面的Schemas下定义 CreateArticleDto
。UpdateArticleDto
的结构是从 CreateArticleDto
定义中自动推断出来的。因此,UpdateArticleDto
也在Swagger中进行了定义。
现在更新create
中的ArticlesService
方法,在数据库中创建一个新项目:
// src/articles/articles.service.ts
@Injectable()
export class ArticlesService {
constructor(private prisma: PrismaService) {
}
create(createArticleDto: CreateArticleDto) {
return 'This action adds a new article';
return this.prisma.article.create({ data: createArticleDto });
}
// ...
}
定义 PATCH /articles/:id 终端节点
此端点用于更新现有文章,此端点的路由处理程序称为 update
。它的实现如下所示:
// src/articles/articles.controller.ts
@Patch(':id')
update(@Param('id') id: string, @Body() updateArticleDto: UpdateArticleDto) {
return this.articlesService.update(+id, updateArticleDto);
}
updateArticleDto
定义被定义为 PartialType
中的 CreateArticleDto
。所以它可以拥有 CreateArticleDto
的所有属性。
// src/articles/dto/update-article.dto.ts
import { PartialType } from '@nestjs/swagger';
import { CreateArticleDto } from './create-article.dto';
export class UpdateArticleDto extends PartialType(CreateArticleDto) {}
与之前一样,您必须更新此操作对应的服务方法:
// src/articles/articles.service.ts
@Injectable()
export class ArticlesService {
constructor(private prisma: PrismaService) {}
// ...
update(id: number, updateArticleDto: UpdateArticleDto) {
return `This action updates a #${id} article`;
return this.prisma.article.update({
where: { id },
data: updateArticleDto,
});
}
// ...
}
article.update
操作将尝试使用给定的 Article
查找 id
记录,并使用 updateArticleDto
的数据更新它。
如果在数据库中找不到此类 Article
记录,Prisma 将返回错误。在这种情况下,API 不会返回用户友好的错误消息。您将在以后的教程中学习NestJS的错误处理。
定义DELETE /articles/:id终端节点
此端点用于删除现有文章,此端点的路由处理程序称为 remove
。它的实现方法如下所示:
// src/articles/articles.controller.ts
@Delete(':id')
remove(@Param('id') id: string) {
return this.articlesService.remove(+id);
}
就像之前一样,转到 ArticlesService
并更新相应的方法:
// src/articles/articles.service.ts
@Injectable()
export class ArticlesService {
constructor(private prisma: PrismaService) { }
// ...
remove(id: number) {
return `This action removes a #${id} article`;
return this.prisma.article.delete({ where: { id } });
}
}
这是针对 articles
端点的最后一个操作。恭喜您的API即将就绪!🎉
在 Swagger 中将终端节点分组在一起
在 @ApiTags
类中添加一个ArticlesController
装饰器,以便在Swagger中将所有articles
端点分组在一起:
// src/articles/articles.controller.ts
import { ApiTags } from '@nestjs/swagger';
@Controller('articles')
@ApiTags('articles')
export class ArticlesController {
// ...
}
现在,该API页面已将articles
端点分组在一起。
更新 Swagger 响应类型
如果您查看 Swagger 中每个端点下的“Responses”选项卡,您会发现“Description”是空的。这是因为 Swagger 还不知道任何端点的响应类型。您将使用一些装饰器来解决这个问题。
首先,您需要定义一个实体,Swagger 可以使用它来标识返回的实体对象的结构。为此,请更新 articles.entity.ts
文件中的 ArticleEntity
类,如下所示:
// src/articles/entities/article.entity.ts
import { Article } from '@prisma/client';
import { ApiProperty } from '@nestjs/swagger';
export class ArticleEntity implements Article {
@ApiProperty()
id: number;
@ApiProperty()
title: string;
@ApiProperty({ required: false, nullable: true })
description: string | null;
@ApiProperty()
body: string;
@ApiProperty()
published: boolean;
@ApiProperty()
createdAt: Date;
@ApiProperty()
updatedAt: Date;
}
这是Prisma Client生成的Article
类型的实现,每个属性都添加了@ApiProperty
装饰器。
现在,是时候使用正确的响应类型对控制器路由处理程序进行注释了。NestJS 有一组用于此目的的装饰器。
// src/articles/articles.controller.ts
import { ApiCreatedResponse, ApiOkResponse, ApiTags } from '@nestjs/swagger';
import { ArticleEntity } from './entities/article.entity';
@Controller('articles')
@ApiTags('articles')
export class ArticlesController {
constructor(private readonly articlesService: ArticlesService) {}
@Post()
@ApiCreatedResponse({ type: ArticleEntity })
create(@Body() createArticleDto: CreateArticleDto) {
return this.articlesService.create(createArticleDto);
}
@Get()
@ApiOkResponse({ type: ArticleEntity, isArray: true })
findAll() {
return this.articlesService.findAll();
}
@Get('drafts')
@ApiOkResponse({ type: ArticleEntity, isArray: true })
findDrafts() {
return this.articlesService.findDrafts();
}
@Get(':id')
@ApiOkResponse({ type: ArticleEntity })
findOne(@Param('id') id: string) {
return this.articlesService.findOne(+id);
}
@Patch(':id')
@ApiOkResponse({ type: ArticleEntity })
update(@Param('id') id: string, @Body() updateArticleDto: UpdateArticleDto) {
return this.articlesService.update(+id, updateArticleDto);
}
@Delete(':id')
@ApiOkResponse({ type: ArticleEntity })
remove(@Param('id') id: string) {
return this.articlesService.remove(+id);
}
}
您为 @ApiOkResponse
、GET
和 PATCH
端点添加了 DELETE
,为 @ApiCreatedResponse
端点添加了 POST
。type
属性用于指定返回类型,您可以在NestJS文档中找到NestJS提供的所有响应装饰器。
现在,Swagger 应该正确定义 API 页面上所有端点的响应类型。
总结和结束语
恭喜!您已经成功使用NestJS构建了一个基础的REST API。在本教程中,您完成了以下内容:
- 使用 NestJS 构建 REST API
- 将 Prisma 顺利集成到 NestJS 项目中
- 使用 Swagger 和 OpenAPI 记录了您的 REST API
本教程的一个重要收获是,使用NestJS和Prisma构建REST API是多么的简单。这是一个非常高效的堆栈,用于快速构建结构良好、类型安全且可维护的后端应用程序。
您可以在GitHub上找到本项目的源代码。如果您在使用过程中遇到任何问题,欢迎随时在仓库中提出问题或提交PR。
原文链接:https://www.prisma.io/blog/nestjs-prisma-rest-api-7D056s1BmOL0