初識 Nest.js
Nest.js官網介紹:
Nest (NestJS) 是一個用於構建高效、可擴展的
Node.js服務器端應用程序的開發框架。它利用JavaScript的漸進增強的能力,使用並完全支持TypeScript(仍然允許開發者使用純 JavaScript 進行開發),並結合了 OOP (面向對象編程)、FP (函數式編程)和 FRP (函數響應式編程)。在底層,Nest 構建在強大的 HTTP 服務器框架上,例如 Express (默認),並且還可以通過配置從而使用 Fastify !
Nest 在這些常見的 Node.js 框架 (Express/Fastify) 之上提高了一個抽象級別,但仍然向開發者直接暴露了底層框架的 API。這使得開發者可以自由地使用適用於底層平台的無數的第三方模塊。
上面這段話剛開始並不能完全理解, 但是簡單可以解讀出來Nest.js的幾個特點:
- 原生支持TypeScript的框架
- 可以基於
Express也可以選擇fastify, 如果你對Express非常熟練, 直接用它的API也是沒問題的
至於其他看不懂,就暫時放一邊, 因為不影響我們入門,后面深入學習后會再來分析。
為什么選擇nest.js?
它通過靈活使用控制反轉、依賴注入和面向切面編程等設計理念,極大的規范了大型應用的架構,降低了模塊之間的耦合度,從而提升了應用的開發效率。在 NodeJS 的世界里,也存在一個全面借鑒 Spring 設計思想的框架
nest與egg簡單對比
- 都是為企業級框架和應用而生
- Egg.js基於Koa,Nest.js基於express
- Egg.js和Nest.js都是按照約定進行開發,Egg相比Nest約定更標准
- 面向對象方面,Nest.js優於Egg.js
Egg特性:
- 高度可擴展的插件
- 內置多進程管理
- 基於Koa,性能優異
- 框架穩定,測試覆蓋率高
Nest特性
- 依賴注入容器
- 模塊化封裝
項目創建
首先確定你已經安裝了Node.js, Node.js 安裝會附帶npx和一個npm 包運行程序。要創建新的Nest.js 應用程序,請在終端上運行以下命令:
npm i -g @nestjs/cli // 全局安裝Nest nest new project-name // 創建項目
執行完創建項目, 會初始化下面這些文件, 並且詢問你要是有什么方式來管理依賴包:

如果你有安裝yarn,可以選擇yarn,能更快一些,npm在國內安裝速度會慢一些

接下來按照提示運行項目:

Nest.js 要求
Node.js(>= 10.13.0,v13 除外), 如果你的
Node.js 版本不滿足要求,可以通過
nvm包管理工具安裝符合要求的
Node.js版本
項目結構
進入項目,看到的目錄結構應該是這樣的:

這里簡單說明一下這些核心文件:
src ├── app.controller.spec.ts ├── app.controller.ts ├── app.module.ts ├── app.service.ts ├── main.ts
app.controller.ts |
單個路由的基本控制器(Controller) |
app.controller.spec.ts |
針對控制器的單元測試 |
app.module.ts |
應用程序的根模塊(Module) |
app.service.ts |
具有單一方法的基本服務(Service) |
main.ts |
應用程序的入口文件,它使用核心函數 NestFactory 來創建 Nest 應用程序的實例。 |
第一個接口
前面我們已經啟動了服務, 那我們怎么查看呢, 首先就是找到入口文件main.ts
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.js的工廠函數NestFactory來創建了一個AppModule實例,啟動了 HTTP 偵聽器,以偵聽main.ts 中所定義的端口。
我們打開瀏覽器訪問http://localhost:3000地址:

這里看到的Hello World就是接口地址http://localhost:9080返回的內容
說明Nest.js創建項目默認就給寫了一個接口例子,那就通過這個接口例子來看,我們應該怎么實現一個接口。
前邊看到mian.ts中也沒有別的文件引入, 只有AppModule, 打開src/app.module.ts:
AppModule是應用程序的根模塊,根模塊提供了用來啟動應用的引導機制,可以包含很多功能模塊。
.mudule文件需要使用一個@Module() 裝飾器的類,裝飾器可以理解成一個封裝好的函數,其實是一個語法糖,@Module() 裝飾器接收四個屬性:providers、controllers、imports、exports。
- providers:
Nest.js注入器實例化的提供者(服務提供者),處理具體的業務邏輯,各個模塊之間可以共享(注入器的概念后面依賴注入部分會講解); - controllers:處理http請求,包括路由控制,向客戶端返回響應,將具體業務邏輯委托給providers處理;
- imports:導入模塊的列表,如果需要使用其他模塊的服務,需要通過這里導入;
- exports:導出服務的列表,供其他模塊導入使用。如果希望當前模塊下的服務可以被其他模塊共享,需要在這里配置導出;
AngularJS、
Spring和
Nest.js都是基於
控制反轉原則設計的,而且都使用了依賴注入的方式來解決解耦問題
app.module.ts中,看到它引入了
app.controller.ts和
app.service.ts
使用@Controller裝飾器來定義控制器, @Get是請求方法的裝飾器,對getHello方法進行修飾, 表示這個方法會被GET請求調用。

從上面,我們可以看出使用@Injectable修飾后的 AppService, 在AppModule中注冊之后,在app.controller.ts中使用,我們就不需要使用new AppService()去實例化,直接引入過來就可以用。
至此,對於http://localhost:3000/接口返回的Hello World邏輯就算理清楚了, 在這基礎上我們再詳細的學習一下Nest.js中的路由使用。
路由裝飾器
Nest.js中沒有單獨配置路由的地方,而是使用裝飾器。Nest.js中定義了若干的裝飾器用於處理路由。
@Controller
如每一個要成為控制器的類,都需要借助@Controller裝飾器的裝飾,該裝飾器可以傳入一個路徑參數,作為訪問這個控制器的主路徑:
對app.controller.ts文件進行修改
通過@Controller("api")修改這個控制器的路由前綴為api, 此時可以通過
HTTP方法處理裝飾器
@Get、@Post、@Put等眾多用於HTTP方法處理裝飾器,經過它們裝飾的方法,可以對相應的HTTP請求進行響應。同時它們可以接受一個字符串或一個字符串數組作為參數,這里的字符串可以是固定的路徑,也可以是通配符。現在我們來兩個簡單的例子
app.service



全局路由前綴
除了上面這些裝飾器可以設置路由外, 我們還可以設置全局路由前綴, 比如給所以路由都加上/api前綴。此時需要修改main.ts
之前在Controller聲明的地址就不用寫了,到此我們認識了Controller、Service、Module、路由以及一些常用的裝飾器
介紹幾個nest-cli提供的幾個有用的命令
//語法 nest g [文件類型] [文件名] [文件目錄]
創建模塊
nest g mo posts
創建一個 posts模塊,文件目錄不寫,默認創建和文件名一樣的posts目錄,在posts目錄下創建一個posts.module.ts
執行完命令后,我們還可以發現同時在根模塊app.module.ts中引入PostsModule這個模塊,也在@Model裝飾器的inports中引入了PostsModule
創建控制器
nest g co posts
此時創建了一個posts控制器,命名為posts.controller.ts以及一個該控制器的單元測試文件
執行完命令, 文件posts.module.ts中會自動引入PostsController,並且在@Module裝飾器的controllers中注入。
創建服務類
nest g service posts
創建app.service.ts文件,並且在app.module.ts文件下,@Module裝飾器的providers中注入注入
其實nest-cli提供的創建命令還有很多, 比如創建過濾器、攔截器和中間件等,可以去官網查看命令
接口格式統一
一般開發中是不會根據HTTP狀態碼來判斷接口成功與失敗的, 而是會根據請求返回的數據,里面加上
code字段
首先定義返回的json格式:
{
"code": 0,
"message": "OK",
"data": {
}
}
請求失敗時返回:
"code": -1,
"message": "我錯了",
"data": {}
}
攔截錯誤請求
首先使用命令創建一個過濾器:
nest g filter core/filter/http-exception
過濾器代碼實現:
import { ArgumentsHost, Catch, ExceptionFilter, HttpException } from '@nestjs/common';
@Catch(HttpException)
export class HttpExceptionFilter implements ExceptionFilter {
catch(exception: HttpException, host: ArgumentsHost) {
const ctx = host.switchToHttp(); // 獲取請求上下文
const response = ctx.getResponse(); // 獲取請求上下文中的 response對象
const status = exception.getStatus(); // 獲取異常狀態碼
// 設置錯誤信息
const message = exception.message
? exception.message
: `${status >= 500 ? 'Service Error' : 'Client Error'}`;
const errorResponse = {
data: {},
message,
code: 200,
};
// 設置返回的狀態碼, 請求頭,發送錯誤信息
response.status(200);
response.header('Content-Type', 'application/json; charset=utf-8');
response.send(errorResponse);
}
}
最后需要在main.ts中全局注冊
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.setGlobalPrefix('api'); // 設置全局路由前綴
app.useGlobalFilters(new HttpExceptionFilter())
await app.listen(3000);
}
這樣對請求錯誤就可以統一的返回了,返回請求錯誤只需要拋出異常即可,比如之前的:
throw new HttpException('拋出', 200);

接下來對請求成功返回的格式進行統一的處理,可以用Nest.js的攔截器來實現。
攔截成功的返回數據
首先使用命令創建一個攔截器:
nest g interceptor core/interceptor/transform
攔截器代碼實現:
import { CallHandler, ExecutionContext, Injectable, NestInterceptor } from '@nestjs/common';
import { map, Observable } from 'rxjs';
@Injectable()
export class TransformInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
return next.handle().pipe(
map((data) => {
return {
data,
code: 200,
msg: '請求成功',
};
}),
);
}
}
最后和過濾器一樣,在main.ts中全局注冊:
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.setGlobalPrefix('api'); // 設置全局路由前綴
app.useGlobalInterceptors(new TransformInterceptor())
app.useGlobalFilters(new HttpExceptionFilter())
await app.listen(3000);
}
過濾器和攔截器實現都是三部曲:創建 > 實現 > 注冊,還是很簡單的
總結
至此我們Nest.js快速上手入門就告一段落了,文章從項目如何搭建,到實現簡單的CRUD,再到統一接口格式、完成接口參數驗證
