feat: add exception filter

This commit is contained in:
Sergey Krylov 2026-01-25 10:50:09 +03:00
parent 353213c6bd
commit 636995bcd1
7 changed files with 104 additions and 0 deletions

View File

@ -5,6 +5,7 @@ import { SwaggerModule } from '@nestjs/swagger'
import { AppModule } from './core/app.module' import { AppModule } from './core/app.module'
import { getCorsConfig, swaggerConfig, validationPipe } from './core/config' import { getCorsConfig, swaggerConfig, validationPipe } from './core/config'
import { GrpcExceptionFilter } from './shared/filters'
async function bootstrap() { async function bootstrap() {
const app = await NestFactory.create(AppModule) const app = await NestFactory.create(AppModule)
@ -13,6 +14,7 @@ async function bootstrap() {
const config = app.get(ConfigService) const config = app.get(ConfigService)
app.useGlobalPipes(validationPipe) app.useGlobalPipes(validationPipe)
app.useGlobalFilters(new GrpcExceptionFilter())
app.enableCors(getCorsConfig(config)) app.enableCors(getCorsConfig(config))

View File

@ -0,0 +1 @@
export * from './rpc-status.enum'

View File

@ -0,0 +1,19 @@
export enum RpcStatus {
OK = 0,
CANCELED = 1,
UNKNOWN = 2,
INVALID_ARGUMENTS = 3,
DEADLINE_EXCEEDED = 4,
NOT_FOUND = 5,
ALREADY_EXISTS = 6,
PERMISION_DENIED = 7,
RESOURSE_EXHAUSED = 8,
FAILED_PRECONDITION = 9,
ABORTED = 10,
OUT_OF_RANGE = 11,
UNIMPLEMENTED = 12,
INTERNAL = 13,
UNAVAILABLE = 14,
DATA_LOSS = 15,
UNAUTHENTICATED = 16
}

View File

@ -0,0 +1,57 @@
import {
ArgumentsHost,
Catch,
ExceptionFilter,
HttpException,
HttpStatus
} from '@nestjs/common'
import type { Response } from 'express'
import { grpcToHttpStatus } from '../utils'
type GrpcException = {
code: number
details: string
}
@Catch()
export class GrpcExceptionFilter implements ExceptionFilter {
public catch(exception: unknown, host: ArgumentsHost) {
console.log('filter host', host)
console.log('filter exception', exception)
const ctx = host.switchToHttp()
const response = ctx.getResponse<Response>()
if (this.isGrpcException(exception)) {
const status =
grpcToHttpStatus[exception.code] || HttpStatus.INTERNAL_SERVER_ERROR
return response.status(status).json({
statusCode: status,
message: exception.details || 'gRPC Error'
})
}
if (exception instanceof HttpException) {
const status = exception.getStatus()
return response.status(status).json({
statusCode: status,
message: exception.message
})
}
return response.status(HttpStatus.INTERNAL_SERVER_ERROR).json({
statusCode: HttpStatus.INTERNAL_SERVER_ERROR,
message: 'Internal Server Error'
})
}
private isGrpcException(exception: unknown): exception is GrpcException {
return Boolean(
exception &&
typeof exception === 'object' &&
'code' in exception &&
'details' in exception
)
}
}

View File

@ -0,0 +1 @@
export * from './grpc-exception.filter'

View File

@ -0,0 +1,23 @@
import { HttpStatus } from '@nestjs/common'
import { RpcStatus } from '../enums'
export const grpcToHttpStatus: Record<number, number> = {
[RpcStatus.OK]: HttpStatus.OK,
[RpcStatus.CANCELED]: 499,
[RpcStatus.UNKNOWN]: HttpStatus.INTERNAL_SERVER_ERROR,
[RpcStatus.INVALID_ARGUMENTS]: HttpStatus.BAD_REQUEST,
[RpcStatus.DEADLINE_EXCEEDED]: HttpStatus.GATEWAY_TIMEOUT,
[RpcStatus.NOT_FOUND]: HttpStatus.NOT_FOUND,
[RpcStatus.ALREADY_EXISTS]: HttpStatus.CONFLICT,
[RpcStatus.PERMISION_DENIED]: HttpStatus.FORBIDDEN,
[RpcStatus.RESOURSE_EXHAUSED]: HttpStatus.TOO_MANY_REQUESTS,
[RpcStatus.FAILED_PRECONDITION]: HttpStatus.BAD_REQUEST,
[RpcStatus.ABORTED]: HttpStatus.CONFLICT,
[RpcStatus.OUT_OF_RANGE]: HttpStatus.BAD_REQUEST,
[RpcStatus.UNIMPLEMENTED]: HttpStatus.NOT_IMPLEMENTED,
[RpcStatus.INTERNAL]: HttpStatus.INTERNAL_SERVER_ERROR,
[RpcStatus.UNAVAILABLE]: HttpStatus.SERVICE_UNAVAILABLE,
[RpcStatus.DATA_LOSS]: HttpStatus.INTERNAL_SERVER_ERROR,
[RpcStatus.UNAUTHENTICATED]: HttpStatus.UNAUTHORIZED
}

View File

@ -0,0 +1 @@
export * from './grpc-to-http-status'