初識 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
,再到統一接口格式、完成接口參數驗證