[ ] Regression
[ ] Bug report
[x] Feature request
[ ] Documentation issue or request
[ ] Support request => Please do not submit support request here, instead post your question on Stack Overflow.
A validation error returns a response with status code 400.
The ValidationPipe
is configurable and accepts a function used to create an error instance which will be thrown.
async function bootstrap () {
const app = await NestFactory.create(AppModule)
app.useGlobalPipes(new ValidationPipe())
await app.listen(3000)
}
Per-field validation errors are, in my opinion, a 422, not a 400. I'd also suggest changing this framework-wide as a default, but that might be a breaking change that people might not welcome with open arms.
"@nestjs/common": "^5.1.0",
"@nestjs/core": "^5.1.0",
"@nestjs/typeorm": "^5.2.2",
What about (2 ways):
1) extending ValidationPipe
and rethrowing different error?
2) creating a filter that transforms BadRequestException
(when ValidationError
) to an instance expected by your application?
Thanks for the response. Here are my attempts at both approaches for reference.
ValidationPipe
export class ValidationPipe422 extends ValidationPipe {
public async transform (value, metadata: ArgumentMetadata) {
try {
return await super.transform(value, metadata)
} catch (e) {
if (e instanceof BadRequestException) {
throw new UnprocessableEntityException(e.message.message)
}
}
}
}
async function bootstrap () {
const app = await NestFactory.create(AppModule)
app.useGlobalPipes(new ValidationPipe422())
}
A small trick: you _need_ return await
here (although usually we can just write return
since async
makes it return a Promise
anyway). Without await
, the function simply returns a (pending) promise properly and thus nothing is caught. With await
, the function stalls until the promise is rejected, the rejection is caught and the catch
block executes.
I'm not sure how to "properly" do the _(when ValidationError
)_ bit from your response. :thinking: I've got the following, which works, but now it will transform every 400
into a 422
.
@Catch(BadRequestException)
export class ValidationExceptionFilter implements ExceptionFilter<BadRequestException> {
public catch (exception, host: ArgumentsHost) {
const ctx = host.switchToHttp()
const response = ctx.getResponse() as express.Response
response
.status(422)
.json({
statusCode: 422,
error: `Unprocessable Entity`,
message: exception.message.message,
})
}
}
async function bootstrap () {
const app = await NestFactory.create(AppModule)
app.useGlobalPipes(new ValidationPipe())
app.useGlobalFilters(new ValidationExceptionFilter())
}
One obvious way to fix this is to inspect the expection.message.message
and see if it quacks like a "400 but I want a 422" error, but I really don't like that one.
As for my initial "feature request": I propose exceptionConstructor
where could just pass in an UnprocessableEntityException
, and then the ValidationPipe
would user whatever I pass it in:
It would default to BadRequest
which would make it a non-breaking change.
Then usage would be just this:
async function bootstrap () {
const app = await NestFactory.create(AppModule)
app.useGlobalPipes(new ValidationPipe({
exceptionConstructor: UnprocessableEntityException,
}))
await app.listen(3000)
}
Since 5.5.0 you can override exception's factory:
new ValidationPipe({
exceptionFactory: (errors: ValidationError[]) =>
new BadRequestException('Validation error'),
});
Since 5.5.0 you can override exception's factory:
new ValidationPipe({ exceptionFactory: (errors: ValidationError[]) => new BadRequestException('Validation error'), });
There's one tiny bit missing that drove me mad... this code would end in throwing "undefined".
Which would lead to an error which can be debugged hardly.
Make sure to use
exceptionFactory: (errors: ValidationError[]) => RETURN new BadRequestException('Validation error'),
... then everything will be fine :)
@jbjhjm It shouldn't, unless you added {
after =>
.
@jbjhjm It shouldn't, unless you added
{
after=>
.
Sometimes I feel stupid. Of course. Didn't notice the brackets were skipped. Thanks for clearing up!
What about passing isDetailedOutputDisabled
flag into exceptionFactory
, otherwise it's unclear to call this.isDetailedOutputDisabled
in the custom exceptionFactory, we do know nothing about context without digging into the source code.
This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.
Most helpful comment
Since 5.5.0 you can override exception's factory: