認識NestJS
用於構建高效且可伸縮的伺服器端應用程式的漸進式 Node.js 框架。以在TypeScript和JavaScript (ES6、ES7、ES8)之上構建高效、可伸縮的企業級伺服器端應用程式。它的核心思想是提供了一個層與層直接的耦合度極小、抽象化極高的一個架構體系。Nest.js目前在行業內具有很高的關注度,所以我們有必要學習一下。
Nest.js基於TypeScript 編寫並且結合了 OOP(物件導向程式設計),FP(函數語言程式設計)和 FRP(函數式響應程式設計)的相關理念。在設計上的很多靈感來自於 Angular,Angular 的很多模式又來自於 Java 中的 Spring 框架,依賴注入、面向切面程式設計等,所以我們也可以認為: Nest.js是Node.js 版的 Spring 框架。
Nest框架底層 HTTP平臺預設是基於 Express 實現的,所以無需擔心第三方庫的缺失,Nest 旨在成為一個與平臺無關的框架。 通過平臺,可以建立可重用的邏輯部件,開發人員可以利用這些部件來跨越多種不同型別的應用程式。 從技術上講,Nest 可以在建立介面卡後使用任何Node HTTP 框架。 有兩個支援開箱即用的 HTTP 平臺:express 和 fastify。無論使用哪種平臺,它都會暴露自己的 API。 它們分別是NestExpressApplication 和 NestFastifyApplication
特點:
完美支援 Typescript
面向 AOP 程式設計
支援 Typeorm
高並行,非同步非阻塞 IO
Node.js 版的 spring
構建微服務應用
安裝NestJS
使用 Nest CLI 建立新專案非常簡單。 只要確保你已經安裝了 npm,然後在你的終端中使用以下命令:
npm i -g @nestjs/cli
nest new project-name
將建立 project 目錄, 安裝node模組和一些其他樣板檔案,並將建立一個 src 目錄,目錄中包含幾個核心檔案。
src
├── app.controller.ts 帶有單個路由的基本控制器範例
├── app.module.ts 應用程式的根模組
└── main.ts 應用程式入口檔案。它使用 NestFactory 用來建立 Nest 應用範例。
程式入口main.ts
main.ts 包含一個非同步函數,它負責引導我們的應用程式:
要建立一個 Nest 應用範例,我們使用了 NestFactory 核心類。NestFactory 暴露了一些靜態方法用於建立應用範例。
create() 方法返回一個實現 INestApplication 介面的物件, 並提供一組可用的方法, 在後面的章節中將對此進行詳細描述。 在上面的main.ts範例中,我們只是啟動 HTTP 伺服器,它允許應用程式等待入站 HTTP 請求。
請注意,使用 Nest CLI 搭建的專案會建立一個初始專案結構,我們鼓勵開發人員將每個模組儲存在自己的專用目錄中。
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
await app.listen(3000);
}
bootstrap();
控制器
控制器負責處理傳入的 請求 和向用戶端返回 響應 。
控制器的目的是接收應用的特定請求。路由機制控制哪個控制器接收哪些請求。通常,每個控制器有多個路由,不同的路由可以執行不同的操作。
為了建立一個基本的控制器,我們使用類和裝飾器。裝飾器將類與所需的後設資料相關聯,並使 Nest 能夠建立路由對映(將請求繫結到相應的控制器)。
在 @Controller() 裝飾器中使用路徑字首可以使我們輕鬆地對一組相關的路由進行分組,並最大程度地減少重複程式碼
要使用 CLI 建立控制器,只需執行 $ nest g controller cats 命令。
取消eslint對分號的限制"@typescript-eslint/prettier.semi":false
匹配路由
控制器是包含路由的
除了在 @Controller() 裝飾器中使用路徑字首可以使我們輕鬆地對一組相關的路由進行分組,
還可以通過請求方式裝飾器進行路由匹配,@Get()、@Post()、@Delete()、@Put()等
同時也可在請求方式裝飾器加上路徑進行路徑匹配
@Get()
query(): string {
return '123'
}
@Post()
sava(): string {
return '123'
}
@Delete()
del(): string {
return '123'
}
@Put()
modify(): string {
return '123'
}
路由引數
引數的獲取常用方式一共有五種:
params 通過新增引數@Param() param
query 通過新增引數@Query() query
application/x-www-form-urlencoded
application/json
multer/form-data
將資料放在body中,同一使用@Body() body
檔案上傳
檔案上傳需要安裝 multer外掛
npm i -D @types/multer
// 單檔案上傳
@Post('upload')
@UseInterceptors(FileInterceptor('file'))
upload(@UploadedFile() file,@Body() body): string|Buffer {
const wirte = createWriteStream(join(__dirname, '..', 'upload', `${file.originalname}`))
wirte.write(file.buffer)
return wirte.path
}
// 多檔案上傳
@Post('upload')
@UseInterceptors(FilesInterceptor('files'))
UploadedFile(@UploadedFiles() files, @Body() body) {
if (!body.name || files.length === 0) {
throw new HttpException('請求引數錯誤.', HttpStatus.FORBIDDEN)
}
for (const file of files) {
const write = createWriteStream(join(__dirname, '..', 'upload', `${body.name}-${Date.now()}-${file.originalname}`))
write.write(file.buffer)
}
}
狀態碼
預設情況狀態碼總是200,除了POST請求(預設響應201狀態碼),我們可以在處理常式外新增@HttpCode(xxx)裝飾器更輕鬆的更改此行為
通常,狀態碼不是固定的,而是取決於各種因素。在這種情況下,您可以使用類庫特有(library-specific)的 response (通過 @Res()注 入 )物件(或者在出現錯誤時,丟擲異常)。
// HttpCode 需要從 @nestjs/common 包匯入。
@Get()
@HttpCode(200)
query(@Query() query): string {
return query;
}
@Get()
query(@Query() query, @Res() res): string {
res.status(200)
return query;
}
重定向
要將響應重定向到特定的 URL,可以使用 @Redirect() 裝飾器或特定於庫的響應物件(並直接呼叫 res.redirect())。
@Redirect() 裝飾器有兩個可選引數,url 和 statusCode。 如果省略,則 statusCode 預設為 302。
@Get()
@Redirect('https://nestjs.com', 301)
有時您可能想動態地決定 HTTP 狀態程式碼或重定向 URL。通過從路由處理方法返回一個如下格式的物件:{ 「url」: string, 「statusCode」: number }
@Get('docs')
@Redirect('https://docs.nestjs.com', 302)
getDocs(@Query('version') version) {
if (version && version === '5') {
return { url: 'https://docs.nestjs.com/v5/' };
}
}
非同步性
我們酷愛現代 Javascript,並且我們知道資料讀取(data extraction)大多是非同步的.這就是為什麼 Nest 完美支援非同步函數(Async Function)特性的原因。
每個非同步函數都必須返回一個 Promise。這意味著您可以返回延遲值, 而 Nest 將自行解析它。
@Get()
async findAll(): Promise<any[]> {
return [];
}
請求負載
此前我們列舉的的 POST 路由處理程式樣例中,處理程式沒有接受任何使用者端引數。我們在這裡通過新增 @Body() 引數來解決這個問題。
首先(如果您使用 TypeScript),我們需要確定 DTO(資料傳輸物件)模式。DTO是一個物件,它定義瞭如何通過網路傳送資料。我們可以通過使用 TypeScript 介面(Interface)或簡單的類(Class)來定義 DTO 模式。有趣的是,我們在這裡推薦使用類。為什麼?類是 JavaScript ES6 標準的一部分,因此它們在編譯後的JavaScript 中被保留為實際實體。另一方面,由於 TypeScript 介面在轉換過程中被刪除,所以 Nest 不能在執行時參照它們。這一點很重要,因為諸如管道(Pipe)之類的特性為在執行時存取變數的元型別提供更多的可能性。
// 建立 CreateCatDto 類
export class CreateCatDto {
readonly name: string;
readonly age: number;
readonly breed: string;
}
@Post()
async create(@Body() createCatDto: CreateCatDto) {
return 'This action adds a new cat';
}