Home > database >  How to catch validation error message - Nestjs Gateway Socket.io
How to catch validation error message - Nestjs Gateway Socket.io

Time:01-08

I have a validation pipe that checks if a user-sent JSON data is valid. Validator is working great, but I can not catch the error and then send it to the client. I know about exeptionFactory property in ValidationPipe constructor, but I still can not catch an error and it still logs in the console.

[Nest] 11820  - 01/07/2023, 11:12:25 PM   ERROR [WsExceptionsHandler] Bad Request Exception
BadRequestException: Bad Request Exception

Here is a code

  @SubscribeMessage(Stream.Transactions)
  @UseGuards(JwtAuthGuard)
  @UsePipes(new ValidationPipe())
  handleTransactions(
    clien: any,
    @MessageBody() data: TransactionObject,
  ) {
    let req = this.streamService.transaction(data)
    return { event: Stream.Transactions, data: req }
  }

CodePudding user response:

I think you can create a filter to get the error and return some specific data. But you can do it in a couple ways: Creating a Websocket exception filter to catch the error or use the exceptionFactory you mention in your question to generate a WsException and catch into the filter.

The main problem (if I'm not wrong) is the pipe does not return a WsException but a BadRequestException.

So to use an exception filter, how the exception is Bad Request, you can use this one:

@Catch(BadRequestException)
export class BadRequestExceptionsFilter extends BaseWsExceptionFilter {
  catch(exception: unknown, host: ArgumentsHost) {
    // Here you have the exception and you can check the data
    const wsException = new WsException(exception.getResponse())
    super.catch(wsException, host);
  }
}

Note how this code follows the documentation

And now not only you can get and read the exception but also you can create a properly WsException.

To use this you can add @UseFilters(BadRequestExceptionsFilter) into your gateway.

Another way is to catch WS and HTTP exceptions and handle properly you want, something similar to this example. The idea can be to catch the HTTP exception only to get Bad Request so your desired context will be always WS:

@Catch(WsException, HttpException)
export class WsAndHttpExceptionFilter {
  public catch(exception: HttpException, host: ArgumentsHost) {
    // Here you have the exception and you can check the data
    const ctx = host.switchToWs()
    const client = ctx.getClient() as WebSocket;
    client.send(JSON.stringify({ /* ... */ }))
  }
}

Or also you can try to create the exceptionFactory to return the WsException.

  • Into the decorator:
@UsePipes(new ValidationPipe({
    exceptionFactory(validationErrors: ValidationError[] = []) {
        // Here are the errors
        if (this.isDetailedOutputDisabled) {
          return new WsException();
        }
        const errors = this.flattenValidationErrors(validationErrors);
        return new WsException(errors);
    }
}))

Check how factories are done into the project and how this code tries to follow the same way but returning WsException.

  • Or in a class extending ValidationPipe:
@Injectable()
export class WSValidationPipe extends ValidationPipe {
  createExceptionFactory() {
    return (validationErrors: ValidationError[] = []) {
        // Here are the errors
        if (this.isDetailedOutputDisabled) {
          return new WsException();
        }
        const errors = this.flattenValidationErrors(validationErrors);
        return new WsException(errors);
    }
  }
}

By the way, you can also @Catch(WsException) (and only this exception which is clearer) once are thrown if it is util for you to return the data you want:

@Catch(WsException)
export class WebsocketExceptionsFilter extends BaseWsExceptionFilter { 
  catch(exception: unknown, host: ArgumentsHost) {
    const ctx = host.switchToWs()
    const client = ctx.getClient() as WebSocket;
    const data = ctx.getData();
    client.send(JSON.stringify({
      event: 'error',
      ok: false,
      error: exception.getError(),
      data: data  // Or whatever you want to add
    }))
  }
}

I can't test now all this code but I've used some of these snippets and works for me, hope it helps.

  • Related