如何在 Nestjs 独立应用程序中使用增强器(管道、守卫、拦截器等)
How to use enhancers (pipes, guards, interceptors, etc) with Nestjs Standalone app
Nestjs 模块系统很棒,但我正在努力弄清楚如何在无服务器设置中充分利用它。
我喜欢在 *.service.ts
文件中编写我的域逻辑的方法,同时使用 *.controller.ts
文件来处理非业务相关的任务,例如验证 HTTP 请求正文并将其转换为在服务中调用方法之前的 DTO。
我在 nestjs 文档中找到了关于 Serverless 的部分,并确定对于我的特定用例,我需要使用“独立应用程序功能”。
我创建了一个sample nestjs app here来说明我的问题。
示例应用程序有一个简单的 add()
函数来添加两个数字。我使用 class-validator
在 AddDto
class.
上进行验证
// add.dto.ts
import { IsNumber } from 'class-validator'
export class AddDto {
@IsNumber()
public a: number;
@IsNumber()
public b: number;
}
然后,通过一些 Nestjs 魔法,我可以通过执行以下操作在我的控制器中使用 AddDto
获得内置验证:
// main.ts
async function bootstrap() {
const app = await NestFactory.create(AppModule);
// Use `ValidationPipe()` for auto-validation in controllers
app.useGlobalPipes(
new ValidationPipe({ transform: true })
)
await app.listen(3000);
}
// app.controller.ts
@Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
@Post('add')
add(@Body() dto: AddDto): number {
// Request body gets auto validated and converted
// to an instance of `AddDto`, sweet!
return this.appService.add(dto.a, dto.b);
}
}
// app.service.ts
@Injectable()
export class AppService {
add(a: number, b: number): number {
return a + b
}
}
到目前为止,还不错。现在在 AWS 中使用 Lambda 函数时会出现问题,即:
- 我想重新使用
app.service.ts
中的业务逻辑
- 我想重新使用在向应用程序发出 HTTP 请求时发生的内置验证,例如上面的示例。
- 我想使用独立应用程序功能,这样我就不必在 Lambda 中启动整个 Nest 服务器
这个问题docs hint:
Be aware that NestFactory.createApplicationContext does not wrap controller methods with enhancers (guard, interceptors, etc.). For this, you must use the NestFactory.create method.
例如,我有一个从 AWS EventBridge 接收消息的 lambda。以下是示例应用程序的片段:
// standalone-app.ts
interface IAddCommand {
a: number;
b: number;
}
export const handler = async (
event: EventBridgeEvent<'AddCommand', IAddCommand>,
context: any
) => {
const appContext = await NestFactory.createApplicationContext(AppModule);
const appService = appContext.get(AppService);
const { a, b } = event.detail;
const sum = appService.add(a, b)
// do work on `sum`, like cache the result, etc...
return sum
};
// lambda-handler.js
const { handler } = require('./dist/standalone-app')
handler({
detail: {
a: "1", // is a string, should be a number
b: "2" // is a string, should be a number
}
})
.then(console.log) // <--- prints out "12" ("1" + "2") instead of "3" (1 + 2)
我在 event.detail
中没有得到事件有效负载的“免费”验证,就像我在向 /add
发出 HTTP POST 请求时对 @Body() dto: AddDto
所做的那样。优选地,代码会在上面的示例中抛出验证错误。相反,我得到了 "12"
的答案——误报。
希望这能说明我问题的症结所在。我仍然想在调用 appService.add(a, b)
之前验证事件的有效负载,但我不想在 app.controller.ts
.
中编写已存在于控制器上的自定义验证逻辑
想法?其他人 运行 以前参与过这个吗?
我在写这个庞大的问题时想到我可以在我的 Lambda 处理程序中简单地使用 class-validator
和 class-transformer
。
import { validateOrReject } from 'class-validator'
import { plainToClass } from 'class-transformer'
import { AddDto } from 'src/dto/add.dto'
export const handler = async (event: any, context: any) => {
const appContext = await NestFactory.createApplicationContext(AppModule);
const appService = appContext.get(AppService);
const data = getPayloadFromEvent(event)
// Convert raw data to a DTO
const dto: AddDto = plainToClass(AddDto, data)
// Validate it!
await validateOrReject(dto)
const sum = appService.add(dto.a, dto.b)
// do work on `sum`...
}
它不像使用 app.useGlobalPipes(new ValidationPipe())
那样“免费”,但只涉及几行额外的代码。
Nestjs 模块系统很棒,但我正在努力弄清楚如何在无服务器设置中充分利用它。
我喜欢在 *.service.ts
文件中编写我的域逻辑的方法,同时使用 *.controller.ts
文件来处理非业务相关的任务,例如验证 HTTP 请求正文并将其转换为在服务中调用方法之前的 DTO。
我在 nestjs 文档中找到了关于 Serverless 的部分,并确定对于我的特定用例,我需要使用“独立应用程序功能”。
我创建了一个sample nestjs app here来说明我的问题。
示例应用程序有一个简单的 add()
函数来添加两个数字。我使用 class-validator
在 AddDto
class.
// add.dto.ts
import { IsNumber } from 'class-validator'
export class AddDto {
@IsNumber()
public a: number;
@IsNumber()
public b: number;
}
然后,通过一些 Nestjs 魔法,我可以通过执行以下操作在我的控制器中使用 AddDto
获得内置验证:
// main.ts
async function bootstrap() {
const app = await NestFactory.create(AppModule);
// Use `ValidationPipe()` for auto-validation in controllers
app.useGlobalPipes(
new ValidationPipe({ transform: true })
)
await app.listen(3000);
}
// app.controller.ts
@Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
@Post('add')
add(@Body() dto: AddDto): number {
// Request body gets auto validated and converted
// to an instance of `AddDto`, sweet!
return this.appService.add(dto.a, dto.b);
}
}
// app.service.ts
@Injectable()
export class AppService {
add(a: number, b: number): number {
return a + b
}
}
到目前为止,还不错。现在在 AWS 中使用 Lambda 函数时会出现问题,即:
- 我想重新使用
app.service.ts
中的业务逻辑
- 我想重新使用在向应用程序发出 HTTP 请求时发生的内置验证,例如上面的示例。
- 我想使用独立应用程序功能,这样我就不必在 Lambda 中启动整个 Nest 服务器
这个问题docs hint:
Be aware that NestFactory.createApplicationContext does not wrap controller methods with enhancers (guard, interceptors, etc.). For this, you must use the NestFactory.create method.
例如,我有一个从 AWS EventBridge 接收消息的 lambda。以下是示例应用程序的片段:
// standalone-app.ts
interface IAddCommand {
a: number;
b: number;
}
export const handler = async (
event: EventBridgeEvent<'AddCommand', IAddCommand>,
context: any
) => {
const appContext = await NestFactory.createApplicationContext(AppModule);
const appService = appContext.get(AppService);
const { a, b } = event.detail;
const sum = appService.add(a, b)
// do work on `sum`, like cache the result, etc...
return sum
};
// lambda-handler.js
const { handler } = require('./dist/standalone-app')
handler({
detail: {
a: "1", // is a string, should be a number
b: "2" // is a string, should be a number
}
})
.then(console.log) // <--- prints out "12" ("1" + "2") instead of "3" (1 + 2)
我在 event.detail
中没有得到事件有效负载的“免费”验证,就像我在向 /add
发出 HTTP POST 请求时对 @Body() dto: AddDto
所做的那样。优选地,代码会在上面的示例中抛出验证错误。相反,我得到了 "12"
的答案——误报。
希望这能说明我问题的症结所在。我仍然想在调用 appService.add(a, b)
之前验证事件的有效负载,但我不想在 app.controller.ts
.
想法?其他人 运行 以前参与过这个吗?
我在写这个庞大的问题时想到我可以在我的 Lambda 处理程序中简单地使用 class-validator
和 class-transformer
。
import { validateOrReject } from 'class-validator'
import { plainToClass } from 'class-transformer'
import { AddDto } from 'src/dto/add.dto'
export const handler = async (event: any, context: any) => {
const appContext = await NestFactory.createApplicationContext(AppModule);
const appService = appContext.get(AppService);
const data = getPayloadFromEvent(event)
// Convert raw data to a DTO
const dto: AddDto = plainToClass(AddDto, data)
// Validate it!
await validateOrReject(dto)
const sum = appService.add(dto.a, dto.b)
// do work on `sum`...
}
它不像使用 app.useGlobalPipes(new ValidationPipe())
那样“免费”,但只涉及几行额外的代码。