不要將Nest.js與Next.js混淆,Nest.js是一種較新且獨特的JavaScript服務(wù)器技術(shù)方法。它基于像Express或Fastify這樣的熟悉服務(wù)器,并添加了許多有用的抽象層,旨在增強和簡化更高級別的應用程序設(shè)計。由于其獨特的編程范式組合、一級TypeScript支持以及內(nèi)置的依賴注入等功能,Nest.js在過去幾年中穩(wěn)步增長,備受歡迎。
Nest.js是JavaScript生態(tài)系統(tǒng)中一項有趣的貢獻,值得我們關(guān)注。在處理服務(wù)器端JavaScript和TypeScript時,它是一個很好的工具。在本文中,我們將快速瀏覽一下Nest.js,示例包括路由、控制器、生產(chǎn)者(依賴注入)和使用guard進行身份驗證。還將了解Nest.js模塊系統(tǒng)。我們的示例是一個用于管理意大利面食譜列表的應用程序。我們將包括一個管理實際數(shù)據(jù)集的依賴注入服務(wù)和一個RESTful API,我們可以使用它列出所有食譜或按ID恢復單個食譜。我們還將設(shè)置一個簡單的經(jīng)過身份驗證的端點,用于添加新食譜。讓我們從搭建一個新項目開始。有了這些,我們就可以深入到例子中了。
設(shè)置Nest.js演示
我們可以使用Nest.js命令行界面來設(shè)置一個快速的應用程序布局,首先要全局安裝Nest.js:. 除了命令行界面,還包括一些有用的功能,如用于共享可重用設(shè)計的模塊。全局安裝使我們可以訪問這些功能以及更多?,F(xiàn)在我們可以使用以下命令創(chuàng)建一個新的應用程序:. 您可以選擇任何您想要的包管理器(, , 或 )。對于這個演示,我將使用。無論使用哪個包管理器,過程都是相同的。 切換到新的目錄并使用以下命令啟動開發(fā)服務(wù)器:. 您可以通過訪問localhost:3000來驗證應用程序是否正在運行,您應該會看到一個“Hello, World!”的消息。這個消息來自。如果您查看該文件,您會看到它使用了一個注入的服務(wù)。讓我們創(chuàng)建一個新的控制器()來返回一個食譜列表,如列表1所示。
列表1:recipe.controller.ts
import { Controller, Get, Inject } from '@nestjs/common';
@Controller('recipes')
export class RecipesController {
@Get()
getRecipes() {
return '[{"name":"Ravioli"}]';
}
}
使用控制器進行路由處理
列表1向我們展示了Nest.js中路由的基礎(chǔ)知識。您可以看到我們使用@Controller注解將類定義為具有路由/recipes的控制器。方法使用@Get()注解來處理GET請求。 目前,這個控制器只是將路由映射到一個硬編碼的響應字符串。在Nest.js提供這個功能之前,我們需要在模塊中注冊新的控制器。模塊是Nest中的另一個重要概念,用于幫助組織應用程序代碼。在我們的例子中,我們需要打開src/app.module.ts并添加控制器,如列表2所示。
列表2:將新的控制器添加到模塊中
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
// Our new controller:
import { RecipesController } from './recipes.controller';
@Module({
imports: [],
controllers: [AppController,RecipesController],
providers: [AppService],
})
export class AppModule {}
Nest.js中的依賴注入框架讓人想起Java生態(tài)系統(tǒng)中的Spring。僅僅擁有內(nèi)置的依賴注入功能就足以讓Nest.js值得考慮,即使沒有其他的附加功能。我們將定義一個服務(wù)提供者,并將其與我們的控制器連接起來。這是一種將應用程序組織成層次結(jié)構(gòu)的清晰方式。您可以在列表3中看到我們的新服務(wù)類RecipesService。
列表3:/src/recipes.service.ts
import { Injectable } from '@nestjs/common';
@Injectable()
export class RecipesService {
private readonly recipes = [
{
name: 'Ravioli',
ingredients: ['pasta', 'cheese', 'tomato sauce'],
},
{
name: 'Lasagna',
ingredients: ['pasta', 'meat sauce', 'cheese'],
},
{
name: 'Spaghetti',
ingredients: ['pasta', 'tomato sauce'],
},
];
getRecipes() {
return this.recipes;
}
}
將服務(wù)添加到模塊中
列表4:將服務(wù)添加到模塊中
@Module({
imports: [],
controllers: [AppController,RecipesController],
providers: [AppService, RecipesService] // add the service
})
模塊是組織應用程序的一種很好的方式。它們可以作為邏輯分組機制,提供一個層次結(jié)構(gòu),其中最基本的模塊被明確定義,其他模塊依賴于它們。
使用服務(wù)
現(xiàn)在我們可以在RecipesController中使用服務(wù),如列表5所示。如果您對依賴注入還不熟悉,這可能看起來像是額外的工作量。但是,隨著系統(tǒng)的發(fā)展,能夠以標準化的方式定義和使用類在整個應用程序中是一個真正的優(yōu)勢,對于應用程序架構(gòu)來說是非常有益的。
列表5:使用RecipesController中的服務(wù)
import { Controller, Get, Inject } from '@nestjs/common';
import { RecipesService } from './recipes.service';
@Controller('recipes')
export class RecipesController {
@Inject()
private readonly recipesService: RecipesService;
@Get()
getRecipes() {
return this.recipesService.getRecipes();
}
}
在Nest中,我們首先導入類,然后使用注解在成員上獲取對它的引用。注入系統(tǒng)會根據(jù)類型將其連接到類的實例上。在Nest中,默認情況下,注入的服務(wù)是單例的,因此所有客戶端類都會獲得對同一個實例的引用??梢允褂闷渌白饔糜颉眮砭氄{(diào)整服務(wù)的實例化方式。除了構(gòu)造函數(shù)注入外,Nest還支持基于屬性的注入?,F(xiàn)在,如果你運行應用程序并訪問localhost:3000/recipes,你將看到來自服務(wù)中的菜譜數(shù)組的JSON輸出。
創(chuàng)建POST端點
現(xiàn)在,讓我們添加一個新的端點,允許用戶添加菜譜。我們將使用Nest.js中稱為guard的最簡單的身份驗證來保護它。Guard位于控制器前面,確定請求的路由方式。您可以在清單6中看到我們簡單的guard。目前,它只是檢查請求中是否有身份驗證頭。
列表6:一個簡單的AuthGuard
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
@Injectable()
export class AuthGuard implements CanActivate {
canActivate(context: ExecutionContext): boolean {
// Simple authentication logic
const request = context.switchToHttp().getRequest();
const authHeader = request.headers.authorization;
return authHeader === 'Bearer secret-token';
}
接下來,向模塊注冊guard,如清單7所示。
列表7:在app.module.ts中注冊這個guard
// ...
import { AuthGuard } from './auth.guard';
@Module({
imports: [],
controllers: [AppController, RecipesController],
providers: [AppService, RecipesService, AuthGuard],
})
export class AppModule {}
現(xiàn)在我們可以使用新的保護服務(wù)來保護端點,如清單8所示。注意新的導入。
列表8:保護POST端點
import { Controller, Get, Inject, Post, Body, UseGuards } from '@nestjs/common';
import { RecipesService } from './recipes.service';
import { AuthGuard } from "./auth.guard";
@Controller('recipes')
export class RecipesController {
@Inject()
private readonly recipesService: RecipesService;
@Get()
getRecipes() {
return this.recipesService.getRecipes();
}
@Post()
@UseGuards(AuthGuard)
addRecipe(@Body() recipe: any) {
return this.recipesService.addRecipe(recipe);
}
}
請注意,注解被用于將新的guard應用于方法,該方法也被注解指定為一個端點。Nest會負責實例化并將guard應用于端點。我們還在服務(wù)中添加了一個非常簡單的方法,如清單9所示。
清單9. /src/recipes.service.ts中的addRecipe方法
addRecipe(recipe: any) {
this.recipes.push(recipe);
return recipe;
}
現(xiàn)在我們可以用兩個CURL請求測試身份驗證和端點,如清單10所示。
列表10:測試新的端點和authguard
$ curl -X POST -H "Authorization: Bearer secret-token" -H "Content-Type: application/json" -d '{"name": "Carbonara", "ingredients": ["pasta", "eggs", "bacon"]}' http://localhost:3000/recipes
{"name":"Carbonara","ingredients":["pasta","eggs","bacon"]}
$ curl -X POST -H "Content-Type: application/json" -d '{"name": "Carbonara", "ingredients": ["pasta", "eggs", "bacon"]}' http://localhost:3000/recipes
{"message":"Forbidden resource","error":"Forbidden","statusCode":403}
可以看到授權(quán)正在工作,因為只有持有標頭的請求才被允許通過。
在TypeScript中使用Nest
到目前為止,我們只使用了一個JavaScript對象。在TypeScript世界中,通常會創(chuàng)建一個模型對象,并將其用作值對象來傳遞信息。例如,我們可以創(chuàng)建這個類(列表11)并在方法中使用它(列表12)。
清單11. Recipe模型
export class Recipe {
constructor(public name: string, public ingredients: string[]) {}
getName(): string {
return this.name;
}
getIngredients(): string[] {
return this.ingredients;
}
}
最后,您可以使該方法具有強類型,Next將自動為我們填充模型對象:addRecipe()POST
列表12:強類型POST方法
import { Injectable } from '@nestjs/common';
import { Recipe } from './recipe.model';
@Injectable()
export class RecipesService {
private readonly recipes: Recipe[] = [
new Recipe('Ravioli', ['pasta', 'cheese', 'tomato sauce']),
new Recipe('Lasagna', ['pasta', 'meat sauce', 'cheese']),
new Recipe('Spaghetti', ['pasta', 'tomato sauce']),
];
getRecipes(): Recipe[] {
return this.recipes;
}
addRecipe(recipe: Recipe): Recipe {
this.recipes.push(recipe);
return recipe;
}
}
然后,您可以將該方法設(shè)置為強類型,Nest將自動為我們填充模型對象,如列表13所示。
列表13:強類型POST方法
// ...
import { Recipe } from './recipe.model';
@Controller('recipes')
export class RecipesController {
// ...
@Post()
@UseGuards(AuthGuard)
addRecipe(@Body() recipe: Recipe) {
const newRecipe = new Recipe(recipe.name, recipe.ingredients);
return this.recipesService.addRecipe(newRecipe);
}
}
結(jié)論
在JavaScript的duck類型和TypeScript的強類型之間的選擇,實際上取決于您、團隊或組織決定使用什么。JavaScript提供了開發(fā)速度和靈活性,而TypeScript提供了結(jié)構(gòu)和更多的工具支持。值得注意的是,Nest.js支持響應式編程,可以從方法和端點返回promises。更重要的是,可以返回一個RxJS Observable。這提供了使用異步數(shù)據(jù)流將應用程序連接在一起的強大選項。雖然我們只是淺嘗輒止,但很明顯,Nest.js是一個經(jīng)過深思熟慮且功能強大的構(gòu)建Node服務(wù)器的平臺。它實現(xiàn)了在Express之上提供更好的架構(gòu)和設(shè)計支持的承諾。如果想構(gòu)建服務(wù)器端JavaScript,尤其是TypeScript應用程序,Nest是一個很好的選擇。
作者:Matthew Tyson
更多技術(shù)干貨盡在wx“云原生數(shù)據(jù)庫”