strong-tie/inbound-calls
0
1<h1 align="center">Fastify</h1>2 3## Validation and Serialization4Fastify uses a schema-based approach, and even if it is not mandatory we5recommend using [JSON Schema](https://json-schema.org/) to validate your routes6and serialize your outputs. Internally, Fastify compiles the schema into a7highly performant function.8 9Validation will only be attempted if the content type is `application-json`, as10described in the documentation for the [content type11parser](./ContentTypeParser.md).12 13All the examples in this section are using the [JSON Schema Draft147](https://json-schema.org/specification-links.html#draft-7) specification.15 16> ## ⚠ Security Notice17> Treat the schema definition as application code. Validation and serialization18> features dynamically evaluate code with `new Function()`, which is not safe to19> use with user-provided schemas. See [Ajv](https://npm.im/ajv) and20> [fast-json-stringify](https://npm.im/fast-json-stringify) for more details.21>22> Regardless the [`$async` Ajv23> feature](https://ajv.js.org/guide/async-validation.html) is supported24> by Fastify, it should not be used as25> part of the first validation strategy. This option is used to access Databases26> and reading them during the validation process may lead to Denial of Service27> Attacks to your application. If you need to run `async` tasks, use [Fastify's28> hooks](./Hooks.md) instead after validation completes, such as `preHandler`.29 30 31### Core concepts32The validation and the serialization tasks are processed by two different, and33customizable, actors:34- [Ajv v8](https://www.npmjs.com/package/ajv) for the validation of a request35- [fast-json-stringify](https://www.npmjs.com/package/fast-json-stringify) for36 the serialization of a response's body37 38These two separate entities share only the JSON schemas added to Fastify's39instance through `.addSchema(schema)`.40 41#### Adding a shared schema42<a id="shared-schema"></a>43 44Thanks to the `addSchema` API, you can add multiple schemas to the Fastify45instance and then reuse them in multiple parts of your application. As usual,46this API is encapsulated.47 48The shared schemas can be reused through the JSON Schema49[**`$ref`**](https://tools.ietf.org/html/draft-handrews-json-schema-01#section-8)50keyword. Here is an overview of _how_ references work:51 52+ `myField: { $ref: '#foo' }` will search for field with `$id: '#foo'` inside the53 current schema54+ `myField: { $ref: '#/definitions/foo' }` will search for field55 `definitions.foo` inside the current schema56+ `myField: { $ref: 'http://url.com/sh.json#' }` will search for a shared schema57 added with `$id: 'http://url.com/sh.json'`58+ `myField: { $ref: 'http://url.com/sh.json#/definitions/foo' }` will search for59 a shared schema added with `$id: 'http://url.com/sh.json'` and will use the60 field `definitions.foo`61+ `myField: { $ref: 'http://url.com/sh.json#foo' }` will search for a shared62 schema added with `$id: 'http://url.com/sh.json'` and it will look inside of63 it for object with `$id: '#foo'`64 65 66**Simple usage:**67 68```js69fastify.addSchema({70 $id: 'http://example.com/',71 type: 'object',72 properties: {73 hello: { type: 'string' }74 }75})76 77fastify.post('/', {78 handler () {},79 schema: {80 body: {81 type: 'array',82 items: { $ref: 'http://example.com#/properties/hello' }83 }84 }85})86```87 88**`$ref` as root reference:**89 90```js91fastify.addSchema({92 $id: 'commonSchema',93 type: 'object',94 properties: {95 hello: { type: 'string' }96 }97})98 99fastify.post('/', {100 handler () {},101 schema: {102 body: { $ref: 'commonSchema#' },103 headers: { $ref: 'commonSchema#' }104 }105})106```107 108#### Retrieving the shared schemas109<a id="get-shared-schema"></a>110 111If the validator and the serializer are customized, the `.addSchema` method will112not be useful since the actors are no longer controlled by Fastify. To access113the schemas added to the Fastify instance, you can simply use `.getSchemas()`:114 115```js116fastify.addSchema({117 $id: 'schemaId',118 type: 'object',119 properties: {120 hello: { type: 'string' }121 }122})123 124const mySchemas = fastify.getSchemas()125const mySchema = fastify.getSchema('schemaId')126```127 128As usual, the function `getSchemas` is encapsulated and returns the shared129schemas available in the selected scope:130 131```js132fastify.addSchema({ $id: 'one', my: 'hello' })133// will return only `one` schema134fastify.get('/', (request, reply) => { reply.send(fastify.getSchemas()) })135 136fastify.register((instance, opts, done) => {137 instance.addSchema({ $id: 'two', my: 'ciao' })138 // will return `one` and `two` schemas139 instance.get('/sub', (request, reply) => { reply.send(instance.getSchemas()) })140 141 instance.register((subinstance, opts, done) => {142 subinstance.addSchema({ $id: 'three', my: 'hola' })143 // will return `one`, `two` and `three`144 subinstance.get('/deep', (request, reply) => { reply.send(subinstance.getSchemas()) })145 done()146 })147 done()148})149```150 151 152### Validation153The route validation internally relies upon [Ajv154v8](https://www.npmjs.com/package/ajv) which is a high-performance JSON Schema155validator. Validating the input is very easy: just add the fields that you need156inside the route schema, and you are done!157 158The supported validations are:159- `body`: validates the body of the request if it is a POST, PUT, or PATCH160 method.161- `querystring` or `query`: validates the query string.162- `params`: validates the route params.163- `headers`: validates the request headers.164 165All the validations can be a complete JSON Schema object (with a `type` property166of `'object'` and a `'properties'` object containing parameters) or a simpler167variation in which the `type` and `properties` attributes are forgone and the168parameters are listed at the top level (see the example below).169 170> ℹ If you need to use the latest version of Ajv (v8) you should read how to do171> it in the [`schemaController`](./Server.md#schema-controller) section.172 173Example:174```js175const bodyJsonSchema = {176 type: 'object',177 required: ['requiredKey'],178 properties: {179 someKey: { type: 'string' },180 someOtherKey: { type: 'number' },181 requiredKey: {182 type: 'array',183 maxItems: 3,184 items: { type: 'integer' }185 },186 nullableKey: { type: ['number', 'null'] }, // or { type: 'number', nullable: true }187 multipleTypesKey: { type: ['boolean', 'number'] },188 multipleRestrictedTypesKey: {189 oneOf: [190 { type: 'string', maxLength: 5 },191 { type: 'number', minimum: 10 }192 ]193 },194 enumKey: {195 type: 'string',196 enum: ['John', 'Foo']197 },198 notTypeKey: {199 not: { type: 'array' }200 }201 }202}203 204const queryStringJsonSchema = {205 type: 'object',206 properties: {207 name: { type: 'string' },208 excitement: { type: 'integer' }209 }210}211 212const paramsJsonSchema = {213 type: 'object',214 properties: {215 par1: { type: 'string' },216 par2: { type: 'number' }217 }218}219 220const headersJsonSchema = {221 type: 'object',222 properties: {223 'x-foo': { type: 'string' }224 },225 required: ['x-foo']226}227 228const schema = {229 body: bodyJsonSchema,230 querystring: queryStringJsonSchema,231 params: paramsJsonSchema,232 headers: headersJsonSchema233}234 235fastify.post('/the/url', { schema }, handler)236```237 238For `body` schema, it is further possible to differentiate the schema per content239type by nesting the schemas inside `content` property. The schema validation240will be applied based on the `Content-Type` header in the request.241 242```js243fastify.post('/the/url', {244 schema: {245 body: {246 content: {247 'application/json': {248 schema: { type: 'object' }249 },250 'text/plain': {251 schema: { type: 'string' }252 }253 // Other content types will not be validated254 }255 }256 }257}, handler)258```259 260*Note that Ajv will try to [coerce](https://ajv.js.org/coercion.html) the values261to the types specified in your schema `type` keywords, both to pass the262validation and to use the correctly typed data afterwards.*263 264The Ajv default configuration in Fastify supports coercing array parameters in265`querystring`. Example:266 267```js268const opts = {269 schema: {270 querystring: {271 type: 'object',272 properties: {273 ids: {274 type: 'array',275 default: []276 },277 },278 }279 }280}281 282fastify.get('/', opts, (request, reply) => {283 reply.send({ params: request.query }) // echo the querystring284})285 286fastify.listen({ port: 3000 }, (err) => {287 if (err) throw err288})289```290 291```sh292curl -X GET "http://localhost:3000/?ids=1293 294{"params":{"ids":["1"]}}295```296 297You can also specify a custom schema validator for each parameter type (body,298querystring, params, headers).299 300For example, the following code disable type coercion only for the `body`301parameters, changing the ajv default options:302 303```js304const schemaCompilers = {305 body: new Ajv({306 removeAdditional: false,307 coerceTypes: false,308 allErrors: true309 }),310 params: new Ajv({311 removeAdditional: false,312 coerceTypes: true,313 allErrors: true314 }),315 querystring: new Ajv({316 removeAdditional: false,317 coerceTypes: true,318 allErrors: true319 }),320 headers: new Ajv({321 removeAdditional: false,322 coerceTypes: true,323 allErrors: true324 })325}326 327server.setValidatorCompiler(req => {328 if (!req.httpPart) {329 throw new Error('Missing httpPart')330 }331 const compiler = schemaCompilers[req.httpPart]332 if (!compiler) {333 throw new Error(`Missing compiler for ${req.httpPart}`)334 }335 return compiler.compile(req.schema)336})337```338 339For further information see [here](https://ajv.js.org/coercion.html)340 341#### Ajv Plugins342<a id="ajv-plugins"></a>343 344You can provide a list of plugins you want to use with the default `ajv`345instance. Note that the plugin must be **compatible with the Ajv version shipped346within Fastify**.347 348> Refer to [`ajv options`](./Server.md#ajv) to check plugins format349 350```js351const fastify = require('fastify')({352 ajv: {353 plugins: [354 require('ajv-merge-patch')355 ]356 }357})358 359fastify.post('/', {360 handler (req, reply) { reply.send({ ok: 1 }) },361 schema: {362 body: {363 $patch: {364 source: {365 type: 'object',366 properties: {367 q: {368 type: 'string'369 }370 }371 },372 with: [373 {374 op: 'add',375 path: '/properties/q',376 value: { type: 'number' }377 }378 ]379 }380 }381 }382})383 384fastify.post('/foo', {385 handler (req, reply) { reply.send({ ok: 1 }) },386 schema: {387 body: {388 $merge: {389 source: {390 type: 'object',391 properties: {392 q: {393 type: 'string'394 }395 }396 },397 with: {398 required: ['q']399 }400 }401 }402 }403})404```405 406#### Validator Compiler407<a id="schema-validator"></a>408 409The `validatorCompiler` is a function that returns a function that validates the410body, URL parameters, headers, and query string. The default411`validatorCompiler` returns a function that implements the412[ajv](https://ajv.js.org/) validation interface. Fastify uses it internally to413speed the validation up.414 415Fastify's [baseline ajv416configuration](https://github.com/fastify/ajv-compiler#ajv-configuration) is:417 418```js419{420 coerceTypes: 'array', // change data type of data to match type keyword421 useDefaults: true, // replace missing properties and items with the values from corresponding default keyword422 removeAdditional: true, // remove additional properties if additionalProperties is set to false, see: https://ajv.js.org/guide/modifying-data.html#removing-additional-properties423 uriResolver: require('fast-uri'),424 addUsedSchema: false,425 // Explicitly set allErrors to `false`.426 // When set to `true`, a DoS attack is possible.427 allErrors: false428}429```430 431This baseline configuration can be modified by providing432[`ajv.customOptions`](./Server.md#factory-ajv) to your Fastify factory.433 434If you want to change or set additional config options, you will need to create435your own instance and override the existing one like:436 437```js438const fastify = require('fastify')()439const Ajv = require('ajv')440const ajv = new Ajv({441 removeAdditional: 'all',442 useDefaults: true,443 coerceTypes: 'array',444 // any other options445 // ...446})447fastify.setValidatorCompiler(({ schema, method, url, httpPart }) => {448 return ajv.compile(schema)449})450```451_**Note:** If you use a custom instance of any validator (even Ajv), you have to452add schemas to the validator instead of Fastify, since Fastify's default453validator is no longer used, and Fastify's `addSchema` method has no idea what454validator you are using._455 456##### Using other validation libraries457<a id="using-other-validation-libraries"></a>458 459The `setValidatorCompiler` function makes it easy to substitute `ajv` with460almost any JavaScript validation library ([joi](https://github.com/hapijs/joi/),461[yup](https://github.com/jquense/yup/), ...) or a custom one:462 463```js464const Joi = require('joi')465 466fastify.post('/the/url', {467 schema: {468 body: Joi.object().keys({469 hello: Joi.string().required()470 }).required()471 },472 validatorCompiler: ({ schema, method, url, httpPart }) => {473 return data => schema.validate(data)474 }475}, handler)476```477 478```js479const yup = require('yup')480// Validation options to match ajv's baseline options used in Fastify481const yupOptions = {482 strict: false,483 abortEarly: false, // return all errors484 stripUnknown: true, // remove additional properties485 recursive: true486}487 488fastify.post('/the/url', {489 schema: {490 body: yup.object({491 age: yup.number().integer().required(),492 sub: yup.object().shape({493 name: yup.string().required()494 }).required()495 })496 },497 validatorCompiler: ({ schema, method, url, httpPart }) => {498 return function (data) {499 // with option strict = false, yup `validateSync` function returns the500 // coerced value if validation was successful, or throws if validation failed501 try {502 const result = schema.validateSync(data, yupOptions)503 return { value: result }504 } catch (e) {505 return { error: e }506 }507 }508 }509}, handler)510```511 512##### .statusCode property513 514All validation errors will be added a `.statusCode` property set to `400`. This guarantees515that the default error handler will set the status code of the response to `400`.516 517```js518fastify.setErrorHandler(function (error, request, reply) {519 request.log.error(error, `This error has status code ${error.statusCode}`)520 reply.status(error.statusCode).send(error)521})522```523 524##### Validation messages with other validation libraries525 526Fastify's validation error messages are tightly coupled to the default527validation engine: errors returned from `ajv` are eventually run through the528`schemaErrorFormatter` function which is responsible for building human-friendly529error messages. However, the `schemaErrorFormatter` function is written with530`ajv` in mind. As a result, you may run into odd or incomplete error messages531when using other validation libraries.532 533To circumvent this issue, you have 2 main options :534 5351. make sure your validation function (returned by your custom `schemaCompiler`)536 returns errors in the same structure and format as `ajv` (although this could537 prove to be difficult and tricky due to differences between validation538 engines)5392. or use a custom `errorHandler` to intercept and format your 'custom'540 validation errors541 542To help you in writing a custom `errorHandler`, Fastify adds 2 properties to all543validation errors:544 545* `validation`: the content of the `error` property of the object returned by546 the validation function (returned by your custom `schemaCompiler`)547* `validationContext`: the 'context' (body, params, query, headers) where the548 validation error occurred549 550A very contrived example of such a custom `errorHandler` handling validation551errors is shown below:552 553```js554const errorHandler = (error, request, reply) => {555 const statusCode = error.statusCode556 let response557 558 const { validation, validationContext } = error559 560 // check if we have a validation error561 if (validation) {562 response = {563 // validationContext will be 'body' or 'params' or 'headers' or 'query'564 message: `A validation error occurred when validating the ${validationContext}...`,565 // this is the result of your validation library...566 errors: validation567 }568 } else {569 response = {570 message: 'An error occurred...'571 }572 }573 574 // any additional work here, eg. log error575 // ...576 577 reply.status(statusCode).send(response)578}579```580 581### Serialization582<a id="serialization"></a>583 584Usually, you will send your data to the clients as JSON, and Fastify has a585powerful tool to help you,586[fast-json-stringify](https://www.npmjs.com/package/fast-json-stringify), which587is used if you have provided an output schema in the route options. We encourage588you to use an output schema, as it can drastically increase throughput and help589prevent accidental disclosure of sensitive information.590 591Example:592```js593const schema = {594 response: {595 200: {596 type: 'object',597 properties: {598 value: { type: 'string' },599 otherValue: { type: 'boolean' }600 }601 }602 }603}604 605fastify.post('/the/url', { schema }, handler)606```607 608As you can see, the response schema is based on the status code. If you want to609use the same schema for multiple status codes, you can use `'2xx'` or `default`,610for example:611```js612const schema = {613 response: {614 default: {615 type: 'object',616 properties: {617 error: {618 type: 'boolean',619 default: true620 }621 }622 },623 '2xx': {624 type: 'object',625 properties: {626 value: { type: 'string' },627 otherValue: { type: 'boolean' }628 }629 },630 201: {631 // the contract syntax632 value: { type: 'string' }633 }634 }635}636 637fastify.post('/the/url', { schema }, handler)638```639You can even have a specific response schema for different content types.640For example:641```js642const schema = {643 response: {644 200: {645 description: 'Response schema that support different content types'646 content: {647 'application/json': {648 schema: {649 name: { type: 'string' },650 image: { type: 'string' },651 address: { type: 'string' }652 }653 },654 'application/vnd.v1+json': {655 schema: {656 type: 'array',657 items: { $ref: 'test' }658 }659 }660 }661 },662 '3xx': {663 content: {664 'application/vnd.v2+json': {665 schema: {666 fullName: { type: 'string' },667 phone: { type: 'string' }668 }669 }670 }671 },672 default: {673 content: {674 // */* is match-all content-type675 '*/*': {676 schema: {677 desc: { type: 'string' }678 }679 }680 }681 }682 }683}684 685fastify.post('/url', { schema }, handler)686```687 688#### Serializer Compiler689<a id="schema-serializer"></a>690 691The `serializerCompiler` is a function that returns a function that must return692a string from an input object. When you define a response JSON Schema, you can693change the default serialization method by providing a function to serialize694every route where you do.695 696```js697fastify.setSerializerCompiler(({ schema, method, url, httpStatus, contentType }) => {698 return data => JSON.stringify(data)699})700 701fastify.get('/user', {702 handler (req, reply) {703 reply.send({ id: 1, name: 'Foo', image: 'BIG IMAGE' })704 },705 schema: {706 response: {707 '2xx': {708 type: 'object',709 properties: {710 id: { type: 'number' },711 name: { type: 'string' }712 }713 }714 }715 }716})717```718 719*If you need a custom serializer in a very specific part of your code, you can720set one with [`reply.serializer(...)`](./Reply.md#serializerfunc).*721 722### Error Handling723When schema validation fails for a request, Fastify will automatically return a724status 400 response including the result from the validator in the payload. As725an example, if you have the following schema for your route726 727```js728const schema = {729 body: {730 type: 'object',731 properties: {732 name: { type: 'string' }733 },734 required: ['name']735 }736}737```738 739and fail to satisfy it, the route will immediately return a response with the740following payload741 742```js743{744 "statusCode": 400,745 "error": "Bad Request",746 "message": "body should have required property 'name'"747}748```749 750If you want to handle errors inside the route, you can specify the751`attachValidation` option for your route. If there is a _validation error_, the752`validationError` property of the request will contain the `Error` object with753the raw `validation` result as shown below754 755```js756const fastify = Fastify()757 758fastify.post('/', { schema, attachValidation: true }, function (req, reply) {759 if (req.validationError) {760 // `req.validationError.validation` contains the raw validation error761 reply.code(400).send(req.validationError)762 }763})764```765 766#### `schemaErrorFormatter`767 768If you want to format errors yourself, you can provide a sync function that must769return an error as the `schemaErrorFormatter` option to Fastify when770instantiating. The context function will be the Fastify server instance.771 772`errors` is an array of Fastify schema errors `FastifySchemaValidationError`.773`dataVar` is the currently validated part of the schema. (params | body |774querystring | headers).775 776```js777const fastify = Fastify({778 schemaErrorFormatter: (errors, dataVar) => {779 // ... my formatting logic780 return new Error(myErrorMessage)781 }782})783 784// or785fastify.setSchemaErrorFormatter(function (errors, dataVar) {786 this.log.error({ err: errors }, 'Validation failed')787 // ... my formatting logic788 return new Error(myErrorMessage)789})790```791 792You can also use [setErrorHandler](./Server.md#seterrorhandler) to define a793custom response for validation errors such as794 795```js796fastify.setErrorHandler(function (error, request, reply) {797 if (error.validation) {798 reply.status(422).send(new Error('validation failed'))799 }800})801```802 803If you want a custom error response in the schema without headaches, and804quickly, take a look at805[`ajv-errors`](https://github.com/epoberezkin/ajv-errors). Check out the806[example](https://github.com/fastify/example/blob/HEAD/validation-messages/custom-errors-messages.js)807usage.808> Make sure to install version 1.0.1 of `ajv-errors`, because later versions of809> it are not compatible with AJV v6 (the version shipped by Fastify v3).810 811Below is an example showing how to add **custom error messages for each812property** of a schema by supplying custom AJV options. Inline comments in the813schema below describe how to configure it to show a different error message for814each case:815 816```js817const fastify = Fastify({818 ajv: {819 customOptions: {820 jsonPointers: true,821 // Warning: Enabling this option may lead to this security issue https://www.cvedetails.com/cve/CVE-2020-8192/822 allErrors: true823 },824 plugins: [825 require('ajv-errors')826 ]827 }828})829 830const schema = {831 body: {832 type: 'object',833 properties: {834 name: {835 type: 'string',836 errorMessage: {837 type: 'Bad name'838 }839 },840 age: {841 type: 'number',842 errorMessage: {843 type: 'Bad age', // specify custom message for844 min: 'Too young' // all constraints except required845 }846 }847 },848 required: ['name', 'age'],849 errorMessage: {850 required: {851 name: 'Why no name!', // specify error message for when the852 age: 'Why no age!' // property is missing from input853 }854 }855 }856}857 858fastify.post('/', { schema, }, (request, reply) => {859 reply.send({860 hello: 'world'861 })862})863```864 865If you want to return localized error messages, take a look at866[ajv-i18n](https://github.com/epoberezkin/ajv-i18n)867 868```js869const localize = require('ajv-i18n')870 871const fastify = Fastify()872 873const schema = {874 body: {875 type: 'object',876 properties: {877 name: {878 type: 'string',879 },880 age: {881 type: 'number',882 }883 },884 required: ['name', 'age'],885 }886}887 888fastify.setErrorHandler(function (error, request, reply) {889 if (error.validation) {890 localize.ru(error.validation)891 reply.status(400).send(error.validation)892 return893 }894 reply.send(error)895})896```897 898### JSON Schema support899 900JSON Schema provides utilities to optimize your schemas that, in conjunction901with Fastify's shared schema, let you reuse all your schemas easily.902 903| Use Case | Validator | Serializer |904|-----------------------------------|-----------|------------|905| `$ref` to `$id` | ️️✔️ | ✔️ |906| `$ref` to `/definitions` | ✔️ | ✔️ |907| `$ref` to shared schema `$id` | ✔️ | ✔️ |908| `$ref` to shared schema `/definitions` | ✔️ | ✔️ |909 910#### Examples911 912##### Usage of `$ref` to `$id` in same JSON Schema913 914```js915const refToId = {916 type: 'object',917 definitions: {918 foo: {919 $id: '#address',920 type: 'object',921 properties: {922 city: { type: 'string' }923 }924 }925 },926 properties: {927 home: { $ref: '#address' },928 work: { $ref: '#address' }929 }930}931```932 933 934##### Usage of `$ref` to `/definitions` in same JSON Schema935```js936const refToDefinitions = {937 type: 'object',938 definitions: {939 foo: {940 $id: '#address',941 type: 'object',942 properties: {943 city: { type: 'string' }944 }945 }946 },947 properties: {948 home: { $ref: '#/definitions/foo' },949 work: { $ref: '#/definitions/foo' }950 }951}952```953 954##### Usage `$ref` to a shared schema `$id` as external schema955```js956fastify.addSchema({957 $id: 'http://foo/common.json',958 type: 'object',959 definitions: {960 foo: {961 $id: '#address',962 type: 'object',963 properties: {964 city: { type: 'string' }965 }966 }967 }968})969 970const refToSharedSchemaId = {971 type: 'object',972 properties: {973 home: { $ref: 'http://foo/common.json#address' },974 work: { $ref: 'http://foo/common.json#address' }975 }976}977```978 979##### Usage `$ref` to a shared schema `/definitions` as external schema980```js981fastify.addSchema({982 $id: 'http://foo/shared.json',983 type: 'object',984 definitions: {985 foo: {986 type: 'object',987 properties: {988 city: { type: 'string' }989 }990 }991 }992})993 994const refToSharedSchemaDefinitions = {995 type: 'object',996 properties: {997 home: { $ref: 'http://foo/shared.json#/definitions/foo' },998 work: { $ref: 'http://foo/shared.json#/definitions/foo' }999 }1000}1001```1002 1003### Resources1004<a id="resources"></a>1005 1006- [JSON Schema](https://json-schema.org/)1007- [Understanding JSON1008 Schema](https://spacetelescope.github.io/understanding-json-schema/)1009- [fast-json-stringify1010 documentation](https://github.com/fastify/fast-json-stringify)1011- [Ajv documentation](https://github.com/epoberezkin/ajv/blob/master/README.md)1012- [Ajv i18n](https://github.com/epoberezkin/ajv-i18n)1013- [Ajv custom errors](https://github.com/epoberezkin/ajv-errors)1014- Custom error handling with core methods with error file dumping1015 [example](https://github.com/fastify/example/tree/master/validation-messages)1016 