NestJS 错误处理模式完全指南
本文讲解 NestJS 错误处理体系:内置 HTTP 异常、异常过滤器(Exception Filter)、自定义异常类、异步错误、RxJS 超时处理与限流防护的生产级实践。
NestJS 的错误处理以"异常过滤器"为核心:抛出的异常被过滤器捕获并转换为 HTTP 响应。业务代码抛内置异常(NotFoundException 等)或自定义异常,全局过滤器统一日志与响应格式,配合 RxJS timeout 处理超时、ThrottlerModule 做限流。
NestJS 的异常分层
NestJS 应用的错误同样分两类:操作错误(预期内的业务失败,如资源不存在、参数非法)和程序错误(bug)。NestJS 内置了一整套 HTTP 异常类对应前者:BadRequestException、UnauthorizedException、NotFoundException、InternalServerErrorException 等,抛出自带正确状态码。
@Get(':id')
async findOne(@Param('id', ParseIntPipe) id: number) {
const user = await this.usersService.findOne(id);
if (!user) {
throw new NotFoundException(`用户 ${id} 不存在`);
}
return user;
}
自定义异常与业务错误码
export class InsufficientBalanceException extends HttpException {
constructor(balance: number, required: number) {
super({
code: 'INSUFFICIENT_BALANCE',
message: `余额不足:当前 ${balance},需要 ${required}`,
balance,
required,
}, HttpStatus.UNPROCESSABLE_ENTITY);
}
}
自定义异常携带业务错误码与上下文数据,前端按 code 做差异化处理——比解析 message 文本可靠得多。
异常过滤器:统一的最后一道闸
// 本片段用于 Express HTTP 适配器;导入 NestJS 类型和 Express Request/Response
@Catch()
export class GlobalExceptionFilter implements ExceptionFilter {
private readonly logger = new Logger(GlobalExceptionFilter.name);
catch(exception: unknown, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse<Response>();
const request = ctx.getRequest<Request>();
const status = exception instanceof HttpException
? exception.getStatus()
: HttpStatus.INTERNAL_SERVER_ERROR;
const raw = exception instanceof HttpException ? exception.getResponse() : null;
const body: Record<string, unknown> = raw && typeof raw === 'object'
? raw as Record<string, unknown> : {};
const message = typeof raw === 'string' ? raw : body.message;
const safeMessage = typeof message === 'string' ||
(Array.isArray(message) && message.every(item => typeof item === 'string'))
? message : '请求失败';
const path = request.path; // 不把查询参数写入响应和默认错误日志
this.logger.error(`${request.method} ${path}`,
exception instanceof Error ? exception.stack : '非 Error 异常');
const business: Record<string, unknown> = {};
if (status < 500) {
for (const key of ['balance', 'required']) {
if (typeof body[key] === 'number') business[key] = body[key];
}
}
response.status(status).json({
...business,
code: status >= 500 ? 'INTERNAL_ERROR'
: typeof body.code === 'string' ? body.code : 'REQUEST_ERROR',
message: status >= 500 ? '服务器内部错误' : safeMessage,
timestamp: new Date().toISOString(),
path,
});
}
}
// main.ts 注册;本例没有需注入的构造器依赖
app.useGlobalFilters(new GlobalExceptionFilter());
该过滤器统一处理进入 Nest HTTP 异常管线且响应尚未发送的错误;掩盖所有 5xx 的内部信息。已发送响应、脱离请求的异步任务和其他协议需要分别处理。
异步错误与 RxJS 超时
NestJS 自动捕获 async handler 的 Promise rejection(比 Express 4 省心)。调用下游服务时用 RxJS 操作符处理超时:
return this.httpService.get(url).pipe(
timeout(5000),
catchError(err => {
if (err.name === 'TimeoutError') {
return throwError(() => new GatewayTimeoutException('下游服务超时'));
}
return throwError(() => err);
}),
);
上面的 Observable 需由控制器返回给 Nest,或用 firstValueFrom 订阅并等待;只创建它不会发起请求。超时也不能保证下游未完成副作用,重试仍需幂等。
限流防护
@nestjs/throttler 模块声明式限流:
@Module({
imports: [ThrottlerModule.forRoot([{ ttl: 60000, limit: 100 }])],
providers: [{ provide: APP_GUARD, useClass: ThrottlerGuard }],
})
需从 @nestjs/core 导入 APP_GUARD,从 @nestjs/throttler 导入模块和 Guard;上例使用当前毫秒单位,60000 为 60 秒。仅注册模块不会启用限流。@Throttle() 可按路由调整,多实例共享计数和可信代理/IP 识别需另行配置。
按业务错误类型观察 NestJS 服务
NestJS 的异常过滤器可统一日志结构与业务错误码。把校验失败、权限拒绝、下游超时分别记录,再在观测云里按接口和版本比较发生率,避免用所有 4xx 的总数代表服务故障。APM 的自动插桩范围取决于 Node.js SDK、NestJS 适配器和底层依赖版本,自定义任务和消息处理路径需要额外验证或埋点。
接入文档:日志采集 · Node.js APM 接入 · 监控器与告警策略。
常见问题(FAQ)
Q:过滤器(Filter)和拦截器(Interceptor)处理错误怎么分工?
拦截器管"正常响应的变换"与 RxJS 流层面的错误加工(如超时转换);过滤器管"异常到 HTTP 响应的最终映射"。超时转异常用拦截器,统一响应格式用过滤器,职责清晰。
Q:GraphQL 或 WebSocket 上下文的异常怎么处理?
HTTP 过滤器不能直接复用为 GraphQL 响应处理器;GraphQL 应按 GqlExceptionFilter 约定返回异常,WebSocket 使用相应上下文和 WsException。多协议应用建议在全局过滤器中按上下文类型分发。
Q:ValidationPipe 的错误怎么统一格式?
配置 exceptionFactory 把校验错误转成自定义异常:new ValidationPipe({ validationError: { target: false, value: false }, exceptionFactory: (errors) => new BadRequestException({ code: 'VALIDATION_FAILED', fields: ... }) }),只从校验错误中提取允许公开的约束信息,递归处理嵌套 children,不能直接返回含请求值的错误对象;若需要返回 fields,也应在过滤器中显式白名单处理。
参考文档
本文依据公开文档整理,示例需按项目依赖版本验证;不将文档中的性能描述视为本项目实测结果。