Skip to content

Commit 6d4fa58

Browse files
committed
feat: Added use422ForValidationErrors.
1 parent 70c1dd8 commit 6d4fa58

File tree

5 files changed

+40
-12
lines changed

5 files changed

+40
-12
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ Register as a plugin, optional providing any of the following options:
2323

2424
- `handle404Errors`: If to set an handler via `setNotFoundHandler`.
2525
- `hideUnhandledErrors`: If to hide unhandled server errors or returning to the client including stack information. Default is to hide errors when `NODE_ENV` environment variable is `production`.
26+
- `use422ForValidationErrors`: If to return `422` (`Unprocessable Entity`) instead of `400` (`Bad Request`) in case of validation errors.
2627
- `convertValidationErrors`: Convert validation errors to a structured human readable object. Default is `true`.
2728
- `convertResponsesValidationErrors`: Convert response validation errors to a structured human readable object. Default is to enable when `NODE_ENV` environment variable is different from `production`.
2829
- `allowUndeclaredResponses`: When converting response validation errors, allow responses that have no schema defined instead of throwing an error.

src/handlers.ts

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,19 @@
11
import { FastifyError, FastifyReply, FastifyRequest } from 'fastify'
22
import {
3-
addAdditionalProperties,
43
BadRequestError,
54
HttpError,
6-
InternalServerError,
75
INTERNAL_SERVER_ERROR,
8-
messagesByCodes,
6+
InternalServerError,
97
NotFoundError,
10-
serializeError,
11-
UnsupportedMediaTypeError
8+
UnprocessableEntityError,
9+
UnsupportedMediaTypeError,
10+
addAdditionalProperties,
11+
messagesByCodes,
12+
serializeError
1213
} from 'http-errors-enhanced'
13-
import { GenericObject, kHttpErrorsEnhancedConfiguration, NodeError, RequestSection } from './interfaces.js'
14+
import { GenericObject, NodeError, RequestSection, kHttpErrorsEnhancedConfiguration } from './interfaces.js'
1415
import { upperFirst } from './utils.js'
15-
import { convertValidationErrors, validationMessagesFormatters, ValidationResult } from './validation.js'
16+
import { ValidationResult, convertValidationErrors, validationMessagesFormatters } from './validation.js'
1617

1718
export function handleNotFoundError(request: FastifyRequest, reply: FastifyReply): void {
1819
handleErrors(new NotFoundError('Not found.'), request, reply)
@@ -26,7 +27,11 @@ export function handleValidationError(error: FastifyError, request: FastifyReque
2627
*/
2728
const section = error.message.match(/^\w+/)![0] as RequestSection
2829

29-
return new BadRequestError('One or more validations failed trying to process your request.', {
30+
const Klass = request[kHttpErrorsEnhancedConfiguration]?.use422ForValidationErrors
31+
? UnprocessableEntityError
32+
: BadRequestError
33+
34+
return new Klass('One or more validations failed trying to process your request.', {
3035
failedValidations: convertValidationErrors(
3136
section,
3237
Reflect.get(request, section),

src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ export const plugin = fastifyPlugin(
2323
convertValidationErrors: options.convertValidationErrors ?? true,
2424
responseValidatorCustomizer: options.responseValidatorCustomizer,
2525
allowUndeclaredResponses: options.allowUndeclaredResponses ?? false,
26+
use422ForValidationErrors: options.use422ForValidationErrors ?? false,
2627
preHandler: typeof options.preHandler === 'function' ? options.preHandler : undefined
2728
}
2829

src/interfaces.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ export interface Configuration {
88
hideUnhandledErrors?: boolean
99
convertValidationErrors?: boolean
1010
allowUndeclaredResponses?: boolean
11+
use422ForValidationErrors?: boolean
1112
responseValidatorCustomizer?: (ajv: Ajv) => void
1213
preHandler?: (error: FastifyError | Error) => Error
1314
}

test/index.test.ts

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,17 @@
22

33
import fastify, { FastifyInstance, FastifyPluginOptions, FastifyReply, FastifyRequest } from 'fastify'
44
import {
5-
BadGatewayError,
65
BAD_GATEWAY,
76
BAD_REQUEST,
8-
createError,
7+
BadGatewayError,
98
INTERNAL_SERVER_ERROR,
109
NOT_FOUND,
11-
UNSUPPORTED_MEDIA_TYPE
10+
UNPROCESSABLE_ENTITY,
11+
UNSUPPORTED_MEDIA_TYPE,
12+
createError
1213
} from 'http-errors-enhanced'
1314
import t from 'tap'
14-
import { handleErrors, plugin as fastifyHttpErrorsEnhanced } from '../src/index.js'
15+
import { plugin as fastifyHttpErrorsEnhanced, handleErrors } from '../src/index.js'
1516

1617
type Callback = () => void
1718

@@ -475,6 +476,25 @@ t.test('Plugin', t => {
475476
})
476477
})
477478

479+
t.test('should use 422 if requested to', async t => {
480+
const server = await buildServer({ use422ForValidationErrors: true })
481+
482+
const response = await server.inject({
483+
method: 'POST',
484+
url: '/validated/abc',
485+
headers: { 'x-header': '123' },
486+
payload: []
487+
})
488+
489+
t.equal(response.statusCode, UNPROCESSABLE_ENTITY)
490+
t.same(JSON.parse(response.payload), {
491+
statusCode: UNPROCESSABLE_ENTITY,
492+
error: 'Unprocessable Entity',
493+
message: 'One or more validations failed trying to process your request.',
494+
failedValidations: { params: { id: 'must be a valid number' } }
495+
})
496+
})
497+
478498
t.test('should validate querystring', async t => {
479499
const server = await buildServer()
480500

0 commit comments

Comments
 (0)