-
-
Notifications
You must be signed in to change notification settings - Fork 12
Description
Prerequisites
- I have written a descriptive issue title
- I have searched existing issues to ensure the feature has not already been requested
🚀 Feature Proposal
Feature
Allow ValidatorCompiler to create compilers with Ajv2019 and Ajv2020.
Proposed approach
Use options.mode to select the version of Ajv to use, consistent with the current API. This choice avoids breaking changes for existing users while letting those who want 2019 or 2020 to use them.
It also means, you can choose 2019 and 2020 for Fastify using an existing option (no changes to Fastify required).
const fastify = Fastify( { ajv: { mode: '2019' } } )
Note on performance
Using the example code without additionalProperties and unevaluatedProperties...
npx autocannon -c 100 -d 5 -p 10 "http://localhost:3000/?s1Prop1=first&extra=hello"
reports average req/sec:
- 52,118.4 (Ajv)
- 53,372.8 (Ajv2019)
- 53,161.6 (Ajv2020)
With unevaluatedProperties:
- -- (Ajv; doesn't support)
- 53,129.6 (Ajv2019)
- 53,827.2 (Ajv2020)
These results show Ajv2019 and Ajv2020 are not always slower than plain Ajv. Measure you use case and choose an answer based on data that applies to it, not unquantified generalizations.
Motivation
When using combining keywords like allOf, anyOf, and oneOf the additionalProperties keyword causes empty query results. See examples below.
The solution to this problem is unevaluatedProperties, which requires Ajv2019 or Ajv2020.
Example
Example 1 shows additionalProperties does not work with default Ajv.
import Fastify from 'fastify'
const fastify = Fastify( { ajv: { mode: undefined } } )
const opts = {
schema: {
querystring: {
type: 'object',
additionalProperties: false,
allOf: [
{
oneOf: [
{
type: 'object',
properties: {
s1Prop1: { type: 'string' },
s1Prop2: { type: 'string', format: 'date' }
},
required: ['s1Prop1'],
},
{
type: 'object',
properties: {
s2Prop1: { type: 'boolean' },
s2Prop2: { type: 'string', format: 'date' }
},
required: ['s2Prop1'],
},
],
},
{
type: 'object',
properties: {
extra: { type: 'string' }
},
required: ['extra']
}
]
},
}
}
fastify.get( '/', opts, ( request, reply ) => {
reply.send( { params: request.query } ) // echo the querystring
} )
fastify.listen( { port: 3000 }, ( err ) => {
if ( err ) throw err
} )curl "localhost:3000/?s1Prop1=first&extra=hello"
-> {"params":{}}
curl "localhost:3000/?s1Prop1=first"
-> {"statusCode":400,"code":"FST_ERR_VALIDATION","error":"Bad Request","message":"querystring must have required property 'extra'"}
curl "localhost:3000/?s1Prop1=first&extra=hello&more=hi"
-> {"params":{}}
Example 2
With proposed changes to allow selecting 2019 or 2020:
- Change
mode: undefinedtomode: '2019'(or '2020') - Change 'additionalProperties: false
tounevaluatedProperties: false`
curl "localhost:3000/?s1Prop1=first&extra=hello"
-> {"params":{"s1Prop1":"first","extra":"hello"}}
curl "localhost:3000/?s1Prop1=first"
-> {"statusCode":400,"code":"FST_ERR_VALIDATION","error":"Bad Request","message":"querystring must have required property 'extra'"}
curl "localhost:3000/?s1Prop1=first&extra=hello&more=hi"
-> {"statusCode":400,"code":"FST_ERR_VALIDATION","error":"Bad Request","message":"querystring must NOT have unevaluated properties"}