├── 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 已經(jīng)為您預(yù)先創(chuàng)建了一些基礎(chǔ)文件,其中幾個尤為關(guān)鍵:

要啟動項目,您可以執(zhí)行以下命令:

npm run start:dev

該命令將監(jiān)視您的文件,每當(dāng)您進(jìn)行更改時自動重新編譯和重新加載服務(wù)器。要驗證服務(wù)器是否正在運(yùn)行,請轉(zhuǎn)到URL http://localhost:3000/。您應(yīng)該會看到一個空頁面,其中包含消息'Hello World!'

注意:在學(xué)習(xí)本教程時,您應(yīng)該保持服務(wù)器在后臺運(yùn)行。

創(chuàng)建 PostgreSQL 實(shí)例

本教程將指導(dǎo)您如何在計算機(jī)上通過 Docker 容器安裝并運(yùn)行 PostgreSQL,以作為 NestJS 應(yīng)用的數(shù)據(jù)庫。

注意:若您不打算使用 Docker,還可以選擇在本地搭建 PostgreSQL 實(shí)例,或在 Heroku 上獲取托管的 PostgreSQL 數(shù)據(jù)庫服務(wù)。

首先,在項目的主文件夾中創(chuàng)建一個docker-compose.yml文件:

touch docker-compose.yml

這個 docker-compose.yml 文件是一個配置文件,它將包含運(yùn)行docker容器的規(guī)范,其中包含PostgreSQL設(shè)置。在文件中創(chuàng)建以下配置:

# 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:

關(guān)于此配置,需要了解以下幾點(diǎn):

請確保您的機(jī)器上沒有其他服務(wù)正在使用 5432 端口。要啟動 postgres 容器,請打開一個新的終端窗口,并在項目的主文件夾中運(yùn)行以下命令:

docker-compose up

如果一切正常,新的終端窗口應(yīng)顯示數(shù)據(jù)庫系統(tǒng)已準(zhǔn)備好接受連接的日志。您應(yīng)該會在終端窗口中看到類似于以下內(nèi)容的日志:

...
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

恭喜 ??。您現(xiàn)在已成功擁有了自己的 PostgreSQL 數(shù)據(jù)庫!

注意:若您關(guān)閉終端窗口,容器也會隨之停止。為避免這種情況,您可以在命令末尾添加 -d 選項,例如:docker-compose up -d。這樣容器就會在后臺持續(xù)運(yùn)行,不會因終端關(guān)閉而中斷。

設(shè)置 Prisma

既然數(shù)據(jù)庫已經(jīng)準(zhǔn)備就緒,接下來就該配置 Prisma 了!

初始化 Prisma

首先,您需要將 Prisma CLI 安裝為項目的開發(fā)依賴項。Prisma CLI 提供了多種命令,方便您與項目進(jìn)行交互。

npm install -D prisma

您可以通過運(yùn)行以下命令在項目中初始化 Prisma:

npx prisma init

這將創(chuàng)建一個新的 Prisma 目錄和一個 schema.prisma 文件。這是包含數(shù)據(jù)庫模式的主配置文件。這個命令也會在你的項目中創(chuàng)建一個.env 文件。

設(shè)置環(huán)境變量

在?.env?文件中,您會發(fā)現(xiàn)一個名為?DATABASE_URL?的環(huán)境變量,它包含一個虛擬的連接字符串。您需要將這個虛擬的連接字符串替換為您 PostgreSQL 實(shí)例的實(shí)際連接字符串。

// .env
DATABASE_URL="postgres://myuser:mypassword@localhost:5432/median-db"

注意:如果您沒有使用 docker(如上一節(jié)所示)來創(chuàng)建 PostgreSQL 數(shù)據(jù)庫,則您的連接字符串將與上面顯示的連接字符串不同。關(guān)于PostgreSQL 的連接字符串格式,您可以在 Prisma 官方文檔中查閱到。

了解 Prisma 架構(gòu)

如果打開 prisma/schema.prisma,您應(yīng)該看到以下默認(rèn)模式:

// prisma/schema.prisma

generator client {
provider = "prisma-client-js"
}

datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}

schema.prisma 文件是使用 Prisma Schema 語言編寫的,這是 Prisma 用于定義數(shù)據(jù)庫架構(gòu)的專有語言。該文件主要包含以下三個核心組成部分:

注意:有關(guān) Prisma 架構(gòu)的更多信息,請查看 Prisma 文檔。

對數(shù)據(jù)進(jìn)行建模

現(xiàn)在,是時候為您的應(yīng)用程序定義數(shù)據(jù)模型了。在本教程中,我們僅需要一個名為?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
}

您已經(jīng)成功創(chuàng)建了一個功能完備的?Article?模型。該模型包含多個字段,如?idtitle?等,每個字段都具備名稱、類型以及可選的特殊屬性(例如?@id@unique?等)。通過在字段類型后添加??,您可以輕松地將某個字段設(shè)置為可選。

id字段被賦予了@id的特殊屬性,這標(biāo)志著它是該模型的主鍵。而?@default(autoincrement())?屬性則確保每當(dāng)有新記錄創(chuàng)建時,該字段都會自動遞增并分配一個唯一的值。

published字段是指示文章是已發(fā)布還是處于草稿模式的標(biāo)志。@default(false)屬性表示默認(rèn)情況下該字段應(yīng)設(shè)置為false

兩個DateTime 字段?createdAt?updatedAt 將跟蹤文章的創(chuàng)建時間和上次更新時間。@updatedAt屬性會自動將該字段更新為當(dāng)前時間戳,無論何時文章被修改,這個字段都會與當(dāng)前時間同步更新。

遷移數(shù)據(jù)庫

定義 Prisma 架構(gòu)后,您將運(yùn)行遷移以在數(shù)據(jù)庫中創(chuàng)建實(shí)際表。要生成并執(zhí)行您的第一個遷移,請在終端中運(yùn)行以下命令:

npx prisma migrate dev --name "init"

此命令將執(zhí)行三項操作:

  1. 保存遷移:Prisma Migrate會首先捕捉您數(shù)據(jù)庫模式的當(dāng)前快照,并據(jù)此分析出將您的數(shù)據(jù)庫模式更新到最新狀態(tài)所需的SQL命令。隨后,Prisma會將這些SQL命令封裝進(jìn)遷移文件中,并將這些文件保存到新創(chuàng)建的 prisma/migrations 文件夾中。這個過程為您的數(shù)據(jù)庫模式變更提供了詳細(xì)的記錄和步驟。
  2. 執(zhí)行遷移:一旦遷移文件被創(chuàng)建,Prisma Migrate就能夠執(zhí)行這些文件中的SQL命令,從而在數(shù)據(jù)庫中實(shí)際創(chuàng)建或更新底層表結(jié)構(gòu)。這個過程確保了您的數(shù)據(jù)庫模式與Prisma模式文件中的定義保持一致。
  3. 生成Prisma客戶端:在遷移執(zhí)行完畢后,Prisma會根據(jù)您最新的數(shù)據(jù)庫模式自動生成Prisma客戶端。如果您尚未安裝客戶端庫,Prisma CLI也會為您自動安裝。您可以在 package.json 文件的 dependencies 部分找到名為 @prisma/client 的包。Prisma Client是一個功能強(qiáng)大的TypeScript查詢生成器,它根據(jù)您的Prisma模式文件自動生成與數(shù)據(jù)庫交互的代碼。這個客戶端是為您的特定Prisma模式量身定制的,它將大大簡化您向數(shù)據(jù)庫發(fā)送查詢的過程。

注意:您可以在 Prisma 文檔中了解有關(guān) Prisma Migrate 的更多信息。

如果成功完成,您應(yīng)該會看到如下消息:

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數(shù)據(jù)庫中創(chuàng)建 Article 表所需的SQL。它是由Prisma根據(jù)您的Prisma模式自動生成和執(zhí)行的。

種子數(shù)據(jù)庫

目前,數(shù)據(jù)庫為空。因此,您將創(chuàng)建一個種子腳本,該腳本將使用一些虛擬數(shù)據(jù)填充數(shù)據(jù)庫。

首先,創(chuàng)建一個名為prisma/seed.ts的文件,這個文件將包含初始化數(shù)據(jù)庫種子所需的模擬數(shù)據(jù)和查詢。

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()函數(shù)來創(chuàng)建兩個項目。upsert函數(shù)只會在沒有文章符合where條件時才創(chuàng)建一個新文章。您選擇使用upsert查詢而不是create查詢,這是因為upsert可以避免因意外嘗試兩次插入相同記錄而產(chǎn)生的錯誤。

您需要指定Prisma在執(zhí)行種子命令時運(yùn)行哪個腳本。您可以通過在 package.json 文件的末尾添加 prisma.seed 鍵來實(shí)現(xiàn)這一點(diǎn):

// package.json

// ...
"scripts": {
// ...
},
"dependencies": {
// ...
},
"devDependencies": {
// ...
},
"jest": {
// ...
},
"prisma": {
"seed": "ts-node prisma/seed.ts"
}

seed 命令將執(zhí)行您之前定義的 prisma/seed.ts 腳本。由于ts-node已經(jīng)作為開發(fā)依賴安裝在您的package.json中,這個命令應(yīng)該能夠自動運(yùn)行。

請使用以下命令來執(zhí)行數(shù)據(jù)庫的種子數(shù)據(jù)初始化:

npx prisma db seed

您應(yīng)該會看到以下輸出:

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 中了解有關(guān)種子設(shè)定的更多信息。

創(chuàng)建 Prisma 服務(wù)

在NestJS應(yīng)用程序中,將Prisma Client API從應(yīng)用邏輯中抽象出來是一個好習(xí)慣。為此,您將創(chuàng)建一個新的服務(wù)來封裝Prisma Client。這個服務(wù)名為 PrismaService,它負(fù)責(zé)創(chuàng)建一個 PrismaClient 實(shí)例并連接到您的數(shù)據(jù)庫。

Nest CLI提供了一種方便的方法,可以直接從命令行界面(CLI)生成模塊和服務(wù)。在終端中運(yùn)行以下命令:

npx nest generate module prisma
npx nest generate service prisma

注 1:如有需要,請參考 NestJS 文檔了解服務(wù)和模塊的介紹。

注 2:在某些情況下,在服務(wù)器已經(jīng)運(yùn)行的情況下運(yùn)行 nest generate 命令可能會導(dǎo)致NestJS拋出異常:Error: Cannot find module './app.controller'。如果您遇到此錯誤,請從終端運(yùn)行以下命令:rm -rf dist并重新啟動服務(wù)器。

這應(yīng)該會生成一個新的帶有./src/prismaprisma.module.ts 文件的文件夾 prisma.service.ts。服務(wù)文件應(yīng)包含以下代碼:

// src/prisma/prisma.service.ts

import { INestApplication, Injectable } from '@nestjs/common';
import { PrismaClient } from '@prisma/client';

@Injectable()
export class PrismaService extends PrismaClient {}

Prisma模塊將負(fù)責(zé)創(chuàng)建 PrismaService 的單例實(shí)例,并允許在整個應(yīng)用程序中共享服務(wù)。要做到這一點(diǎn),你需要將 PrismaService 添加到 exports 文件中的 prisma.module.ts 數(shù)組中:

// src/prisma/prisma.module.ts

import { Module } from '@nestjs/common';
import { PrismaService } from './prisma.service';

@Module({
providers: [PrismaService],
exports: [PrismaService],
})
export class PrismaModule {}

現(xiàn)在,任何導(dǎo)入PrismaModule的模塊都可以訪問PrismaService,并將其注入到自己的組件/服務(wù)中。這是NestJS應(yīng)用程序的常見模式。

完成后,您就完成了 Prisma 的設(shè)置!您現(xiàn)在可以開始構(gòu)建 REST API。

設(shè)置 Swagger

Swagger 是一個基于 OpenAPI 規(guī)范來記錄 API 的工具。Nest 提供了一個專門的 Swagger 模塊,您很快就會用到它。

首先安裝所需的依賴項:

npm install --save @nestjs/swagger swagger-ui-express

現(xiàn)在打開 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();

當(dāng)應(yīng)用程序運(yùn)行時,打開瀏覽器并導(dǎo)航到 http://localhost:3000/api. 您應(yīng)該能夠看到 Swagger UI 界面。

Swagger 用戶界面

為Article模型實(shí)現(xiàn) CRUD 操作

在本節(jié)中,您將為Article模型實(shí)現(xiàn)創(chuàng)建(Create)、讀取(Read)、更新(Update)和刪除(Delete)操作,以及任何相關(guān)的業(yè)務(wù)邏輯。

生成 REST 資源

在您能夠?qū)崿F(xiàn)REST API之前,您需要為Article模型生成REST資源。這可以通過Nest CLI快速完成。在終端中運(yùn)行以下命令

npx nest generate resource

您將收到一些 CLI 提示。請相應(yīng)地回答問題:

  1. 您想為這個資源使用什么名稱(復(fù)數(shù)形式,例如,“users”)? articles
  2. 您使用的傳輸層是什么? REST API
  3. 您是否希望生成CRUD入口點(diǎn)? 是

現(xiàn)在您應(yīng)該在src/articles目錄下找到了所有REST端點(diǎn)的樣板代碼。在src/articles/articles.controller.ts文件中,您將看到不同路由(也稱為路由處理器)的定義。處理每個請求的業(yè)務(wù)邏輯封裝在src/articles/articles.service.ts文件中。目前,這個文件包含一些虛擬實(shí)現(xiàn)。

如果您再次打開 Swagger API 頁面,您應(yīng)該會看到如下內(nèi)容:

自動生成的 “articles” 端點(diǎn)


SwaggerModule會搜索所有路由處理器上的@Body()、@Query()和@Param()裝飾器,以生成這個API頁面。

將PrismaClient添加到Articles模塊

要在Articles模塊內(nèi)訪問PrismaClient,您必須將PrismaModule作為一個導(dǎo)入添加。在ArticlesModule中添加以下導(dǎo)入:

// 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 {}

您現(xiàn)在可以將 PrismaService 注入到 ArticlesService 中,并使用它來訪問數(shù)據(jù)庫。為此,您需要在articles.service.ts文件中添加一個構(gòu)造函數(shù),如下所示:

// 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 終端節(jié)點(diǎn)

該端點(diǎn)的控制器名為 findAll。此端點(diǎn)將返回數(shù)據(jù)庫中所有已發(fā)布的文章。findAll 控制器的實(shí)現(xiàn)如下所示:

// src/articles/articles.controller.ts

@Get()
findAll() {
return this.articlesService.findAll();
}

您需要更新 ArticlesService.findAll()以返回數(shù)據(jù)庫中所有已發(fā)布文章的數(shù)組:

// 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 并點(diǎn)擊 GET /articles 菜單來測試這個端點(diǎn)。點(diǎn)擊“Try it out”,然后點(diǎn)擊“Execute”按鈕來查看結(jié)果。

注意:您也可以直接在瀏覽器中運(yùn)行所有請求,或者通過REST客戶端(如Postman)運(yùn)行所有請求。Swagger還為每個請求生成curl命令,以防您希望在終端中運(yùn)行HTTP請求。

定義 GET /articles/drafts 終端節(jié)點(diǎn)

您將定義一個新的路由來獲取所有未發(fā)布的文章。NestJS 沒有為這個端點(diǎn)自動生成控制器路由處理器,因此您需要自行編寫它。

// 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 {}

你的編輯器應(yīng)該顯示一個錯誤,提示沒有名為articlesService.findDrafts()的函數(shù)存在。要解決這個問題,請在 findDrafts 中實(shí)現(xiàn) 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 端點(diǎn)現(xiàn)在應(yīng)該已經(jīng)在 Swagger API 頁面中可用了。

注意:我建議您在完成實(shí)施后通過 Swagger API 頁面測試每個終端節(jié)點(diǎn)。

定義 GET /articles/:id 終端節(jié)點(diǎn)

此端點(diǎn)的控制器路由處理器名為findOne。它的實(shí)現(xiàn)如下所示:

// src/articles/articles.controller.ts

@Get(':id')
findOne(@Param('id') id: string) {
return this.articlesService.findOne(+id);
}

路由接受一個動態(tài)的id參數(shù),該參數(shù)被傳遞給findOne控制器路由處理程序。由于Article模型具有整數(shù)id字段,因此需要使用id運(yùn)算符將+參數(shù)轉(zhuǎn)換為數(shù)字。

現(xiàn)在,更新 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,然后點(diǎn)擊 GET /articles/{id} 下拉菜單。點(diǎn)擊“Try it out”(試用),在 id 參數(shù)中輸入一個有效的值,接著點(diǎn)擊“Execute”(執(zhí)行)來查看結(jié)果。

定義POST /articles終端節(jié)點(diǎn)

這是創(chuàng)建新文章的端點(diǎn),此端點(diǎn)的控制器路由處理程序稱為 create。它的實(shí)現(xiàn)如下所示:

// src/articles/articles.controller.ts

@Post()
create(@Body() createArticleDto: CreateArticleDto) {
return this.articlesService.create(createArticleDto);
}

請注意,它在請求體中期望一個類型為CreateArticleDto的參數(shù)。DTO(Data Transfer Object,數(shù)據(jù)傳輸對象)是一個定義數(shù)據(jù)如何通過網(wǎng)絡(luò)發(fā)送的對象。目前,CreateArticleDto是一個空類。您需要向其中添加屬性,以定義請求正文的結(jié)構(gòu)。復(fù)制再試一次分享

// 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 可見。更多關(guān)于這方面的信息可以在NestJS文檔中找到。

現(xiàn)在,您應(yīng)該在Swagger API頁面的Schemas下定義 CreateArticleDtoUpdateArticleDto 的結(jié)構(gòu)是從 CreateArticleDto 定義中自動推斷出來的。因此,UpdateArticleDto 也在Swagger中進(jìn)行了定義。

現(xiàn)在更新create中的ArticlesService方法,在數(shù)據(jù)庫中創(chuàng)建一個新項目:

// 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 終端節(jié)點(diǎn)

此端點(diǎn)用于更新現(xiàn)有文章,此端點(diǎn)的路由處理程序稱為 update。它的實(shí)現(xiàn)如下所示:

// 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) {}

與之前一樣,您必須更新此操作對應(yīng)的服務(wù)方法:

// 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的數(shù)據(jù)更新它。

如果在數(shù)據(jù)庫中找不到此類 Article 記錄,Prisma 將返回錯誤。在這種情況下,API 不會返回用戶友好的錯誤消息。您將在以后的教程中學(xué)習(xí)NestJS的錯誤處理。

定義DELETE /articles/:id終端節(jié)點(diǎn)

此端點(diǎn)用于刪除現(xiàn)有文章,此端點(diǎn)的路由處理程序稱為 remove。它的實(shí)現(xiàn)方法如下所示:

// src/articles/articles.controller.ts

@Delete(':id')
remove(@Param('id') id: string) {
return this.articlesService.remove(+id);
}

就像之前一樣,轉(zhuǎn)到 ArticlesService 并更新相應(yīng)的方法:

// 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 端點(diǎn)的最后一個操作。恭喜您的API即將就緒!??

在 Swagger 中將終端節(jié)點(diǎn)分組在一起

@ApiTags 類中添加一個ArticlesController裝飾器,以便在Swagger中將所有articles端點(diǎn)分組在一起:

// src/articles/articles.controller.ts

import { ApiTags } from '@nestjs/swagger';

@Controller('articles')
@ApiTags('articles')
export class ArticlesController {
// ...
}

現(xiàn)在,該API頁面已將articles端點(diǎn)分組在一起。

更新 Swagger 響應(yīng)類型

如果您查看 Swagger 中每個端點(diǎn)下的“Responses”選項卡,您會發(fā)現(xiàn)“Description”是空的。這是因為 Swagger 還不知道任何端點(diǎn)的響應(yīng)類型。您將使用一些裝飾器來解決這個問題。

首先,您需要定義一個實(shí)體,Swagger 可以使用它來標(biāo)識返回的實(shí)體對象的結(jié)構(gòu)。為此,請更新 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類型的實(shí)現(xiàn),每個屬性都添加了@ApiProperty裝飾器。

現(xiàn)在,是時候使用正確的響應(yīng)類型對控制器路由處理程序進(jìn)行注釋了。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);
}
}

您為 @ApiOkResponseGETPATCH 端點(diǎn)添加了 DELETE ,為 @ApiCreatedResponse 端點(diǎn)添加了 POSTtype屬性用于指定返回類型,您可以在NestJS文檔中找到NestJS提供的所有響應(yīng)裝飾器。

現(xiàn)在,Swagger 應(yīng)該正確定義 API 頁面上所有端點(diǎn)的響應(yīng)類型。

總結(jié)和結(jié)束語

恭喜!您已經(jīng)成功使用NestJS構(gòu)建了一個基礎(chǔ)的REST API。在本教程中,您完成了以下內(nèi)容:

本教程的一個重要收獲是,使用NestJS和Prisma構(gòu)建REST API是多么的簡單。這是一個非常高效的堆棧,用于快速構(gòu)建結(jié)構(gòu)良好、類型安全且可維護(hù)的后端應(yīng)用程序。

您可以在GitHub上找到本項目的源代碼。如果您在使用過程中遇到任何問題,歡迎隨時在倉庫中提出問題或提交PR。

原文鏈接:https://www.prisma.io/blog/nestjs-prisma-rest-api-7D056s1BmOL0

熱門推薦
一個賬號試用1000+ API
助力AI無縫鏈接物理世界 · 無需多次注冊
3000+提示詞助力AI大模型
和專業(yè)工程師共享工作效率翻倍的秘密
返回頂部
上一篇
設(shè)計和開發(fā)Web API的最佳實(shí)踐
下一篇
使用NestJS和Prisma構(gòu)建REST API:處理關(guān)系型數(shù)據(jù)
国内精品久久久久影院日本,日本中文字幕视频,99久久精品99999久久,又粗又大又黄又硬又爽毛片
在线欧美小视频| 国产aⅴ综合色| 91精品国产欧美一区二区18| 91麻豆精品国产| 亚洲影视资源网| 日本电影欧美片| av资源站一区| 国产午夜亚洲精品午夜鲁丝片| 视频一区视频二区中文| 亚洲最新在线观看| 高清在线成人网| 国产成人精品免费视频网站| 日韩三级免费观看| 国产精品18久久久久久vr| 在线观看91视频| 国产精品一区二区久久不卡| 欧美精品久久久久久久久老牛影院| 一区二区三区国产豹纹内裤在线| 欧美日韩免费一区二区三区视频| 欧美人牲a欧美精品| 精品在线观看免费| 亚洲欧美日韩一区二区 | 成人激情小说网站| 国产精品乱码妇女bbbb| 欧美va亚洲va在线观看蝴蝶网| zzijzzij亚洲日本少妇熟睡| 亚洲精品第一国产综合野| 国产精品自产自拍| 国产精品自拍av| 国产精品少妇自拍| 精品日韩一区二区三区 | 国产一区二区中文字幕| 蜜桃av噜噜一区| 亚洲欧美日韩人成在线播放| 国产精品沙发午睡系列990531| 精品日韩一区二区三区免费视频| 国产精品正在播放| 国产成人福利片| 国产成人av一区二区三区在线 | 在线视频国内自拍亚洲视频| 亚洲午夜电影网| 日本伊人午夜精品| 国产精品12区| 久久国产精品露脸对白| 日本欧美韩国一区三区| 韩国欧美国产一区| 色88888久久久久久影院野外| 成人av在线播放网站| 欧美亚州韩日在线看免费版国语版| 4438x亚洲最大成人网| 精品理论电影在线观看| 日韩免费看的电影| 欧美美女直播网站| 久久影院视频免费| 亚洲综合区在线| 成人免费毛片嘿嘿连载视频| 色综合天天综合色综合av| 国产欧美精品一区| 日本强好片久久久久久aaa| 色呦呦日韩精品| 亚洲日本一区二区三区| 欧洲精品一区二区| 日韩精品三区四区| 成人美女视频在线看| 日韩一区二区三| 亚洲精品成人悠悠色影视| 国产91精品一区二区麻豆网站| 日韩西西人体444www| 一区二区三区91| 亚洲第一综合色| 欧美影院一区二区| 香蕉久久一区二区不卡无毒影院| 欧美日韩亚洲综合在线 欧美亚洲特黄一级| 欧美精品久久久久久久多人混战 | 国产亚洲成av人在线观看导航| 国产精品国产三级国产普通话三级| 亚洲精品视频在线观看免费| 在线观看av一区| 欧美精彩视频一区二区三区| 免费看精品久久片| 26uuuu精品一区二区| 欧美三级在线播放| 久久精品99国产精品日本| 7777精品伊人久久久大香线蕉最新版| 精品国产不卡一区二区三区| www.亚洲色图| 国模一区二区三区白浆| 麻豆一区二区在线| 成人动漫中文字幕| 韩国成人福利片在线播放| 亚洲青青青在线视频| 欧美激情在线看| 精品国产一区二区三区忘忧草| 丰满少妇久久久久久久| 国产精品久久久一本精品| 久久久久久**毛片大全| 久久精品亚洲国产奇米99| 成人免费视频视频| 夜夜亚洲天天久久| 日本福利一区二区| 成人小视频免费在线观看| 国产精品美女久久久久久久| 一本在线高清不卡dvd| 国产精品一区二区三区网站| 国产一区视频在线看| 日韩中文欧美在线| 天天综合色天天| 天涯成人国产亚洲精品一区av| 精品久久久久久最新网址| 99国产精品久| 欧美系列一区二区| 日韩一区二区影院| 亚洲欧洲综合另类| 经典三级视频一区| 亚洲超碰精品一区二区| 久久亚洲精精品中文字幕早川悠里| 色狠狠一区二区| 久久精品欧美日韩| 樱桃视频在线观看一区| 久久久久九九视频| 国产精品久久免费看| 国产91丝袜在线观看| 9191成人精品久久| 自拍偷拍亚洲欧美日韩| 大胆亚洲人体视频| 在线日韩av片| 色综合中文字幕国产| 欧美激情在线一区二区三区| 国产剧情在线观看一区二区| 久久久久九九视频| 日本精品一区二区三区高清| 国产欧美一区二区精品性色超碰| 美女视频黄久久| 日韩制服丝袜先锋影音| 欧美国产精品中文字幕| 一本久道中文字幕精品亚洲嫩| 国产精品久久毛片a| 欧美精品99久久久**| 久久精品久久精品| 亚洲丝袜精品丝袜在线| 成人三级在线视频| 日韩精品电影一区亚洲| 99久久伊人网影院| 另类专区欧美蜜桃臀第一页| 中文字幕不卡的av| 欧美午夜一区二区三区| 国产精品国产三级国产aⅴ中文 | 欧美精品一二三四| 国产福利91精品一区| 国产一区二区日韩精品| 亚洲另类一区二区| 国产精品久久看| 国产精品素人视频| 日本一区二区电影| 欧美色男人天堂| 欧美精品一区男女天堂| 99精品欧美一区二区蜜桃免费| 国模大尺度一区二区三区| 黄一区二区三区| 国产91高潮流白浆在线麻豆| 国产成人亚洲精品青草天美 | 久久久久国产精品厨房| 日韩小视频在线观看专区| 欧美私模裸体表演在线观看| 色久优优欧美色久优优| 欧美性xxxxxxxx| 久久国内精品视频| 久久福利视频一区二区| 国产一区二区不卡在线| 国产精一区二区三区| 国产麻豆精品一区二区| 国产精品66部| 精品999在线播放| 亚洲一区二区中文在线| 久久er99热精品一区二区| 日本成人在线看| 精品日产卡一卡二卡麻豆| 伊人一区二区三区| 成人高清视频在线| 精品88久久久久88久久久| 欧美日韩电影在线| 中文av一区特黄| 久久久久成人黄色影片| 亚洲精品网站在线观看| 青娱乐精品在线视频| 蜜芽一区二区三区| 欧美日韩一区二区不卡| www.日韩精品| 99国产一区二区三精品乱码| 久久久蜜臀国产一区二区| 久久精品99国产精品| 毛片不卡一区二区| 91精品国产一区二区三区| 亚洲成a人v欧美综合天堂| 免费一区二区视频| 国产成人免费网站| 国产亚洲成aⅴ人片在线观看| 天天操天天色综合| 在线视频一区二区三区| 日韩精品高清不卡|