fastify / fastify

Fast and low overhead web framework, for Node.js

Home Page:https://www.fastify.dev

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Handling and validation of optional null values in JSON body

lundibundi opened this issue · comments

Prerequisites

  • I have written a descriptive issue title
  • I have searched existing issues to ensure the bug has not already been reported

Fastify version

4.26.2 / test on master

Plugin version

No response

Node.js version

20.x

Operating system

macOS

Operating system version (i.e. 20.04, 11.3, 10)

14.2.1

Description

Currently I observe that the validation of JSON body with null value silently passes without throwing any error even when null is not allowed by JSON schema. And which is worse replaces that null with default value. e.g. if field is defined as type: string in JSON schema the value will be replaced with empty string ''.

I've tried to provide custom ajv instance to fastify with removeAdditional: true, coerceTypes: false but that didn't help. I guess it is not used for validation directly.
Upon testing this case with raw ajv the results were inconsistent. With simple schema with optional fields it just silently passed the null. With schema with anyOf it sometimes breaks the value and removed valid fields...
So the issue might be with ajv as well.

I've prepared a test case below similar to the ones in schema-validation.test.js.

Steps to Reproduce

I've prepared a test case based on ones in schema-validation.test.js.

test('Validation of optional null body params test', t => {
  t.plan(3)

  const fastify = Fastify()
  fastify.post('/', {
    schema: {
      body: {
        type: 'object',
        properties: {
          name: { type: 'string' },
          work: { type: 'string' }
        },
        required: ['name']
      }
    }
  }, function (req, reply) {
    reply.code(200).send(req.body)
  })

  fastify.inject({
    method: 'POST',
    payload: {
      name: 'michelangelo',
      work: null
    },
    url: '/'
  }, (err, res) => {
    t.error(err)
    t.same(res.json(), { statusCode: 400, code: 'FST_ERR_VALIDATION', error: 'Bad Request', message: 'body/work must be string' })
    t.equal(res.statusCode, 400)
  })
})

Currently this test case fails with the following:
image

Meaning that the work: null gets coerced to ''.

Expected Behavior

I'd expect the validation to either throw (because this is not valid value by JSON schema) or at least remove it from body. But not replace it with default value.

Please clarify where should I look into the fix, do you think this can be a fastify or ajv issue?
I'd gladly work on a fix once I understand where to look.

Interesting, it should throw unless nullable is enabled

This seems the correct behavior of coerceTypes.

I've tried to provide custom ajv instance to fastify with removeAdditional: true, coerceTypes: false but that didn't help. I guess it is not used for validation directly.

How did you test those?

@mcollina thanks for absolute on point question. Looks like I didn't pass the ajv setup to the test server which caused this to happen. After that the behavior is as I expected - throw with 400.

I guess then the only question remaining is for default setup. Looking at https://fastify.dev/docs/latest/Reference/Validation-and-Serialization/#schema-validator I assumed that coerceTypes: 'array' is coerce arrays only which is not correct. And that is actually coerce everything and arrays. So resolving this now.

Adding server setup for reference:

  fastify({
    pluginTimeout: 25_000,
    ajv: {
      customOptions: {
        useDefaults: true,
        removeAdditional: true,
        coerceTypes: false,
        allErrors: false,
        allowUnionTypes: true,
      },
      plugins: [ajvFormats, ajvKeywords],
    },
  });

Thanks for quick response.