What version of Ajv are you using? Does the issue happen if you use the latest version?
6.8.1
Ajv options object
{nullable: true}
JSON Schema
{"type":"string", "nullable": false}
Sample data
{"hello": "worl"}
Your code
var Ajv = require('ajv');
var ajv = new Ajv({nullable: true});
var valid = ajv.validate({"type":"string", "nullable": false}, { 'hello': "worl" });
Validation result, data AFTER validation, error messages
Error: keyword schema is invalid: data should be equal to constant
at Object.useCustomRule (/Users/matt/staging/cabochon/packages/monolith/node_modules/ajv/lib/compile/index.js:264:20)
at Object.generate_custom [as code] (/Users/matt/staging/cabochon/packages/monolith/node_modules/ajv/lib/dotjs/custom.js:32:24)
at Object.generate_validate [as validate] (/Users/matt/staging/cabochon/packages/monolith/node_modules/ajv/lib/dotjs/validate.js:353:35)
at Object.generate_properties [as code] (/Users/matt/staging/cabochon/packages/monolith/node_modules/ajv/lib/dotjs/properties.js:195:26)
at generate_validate (/Users/matt/staging/cabochon/packages/monolith/node_modules/ajv/lib/dotjs/validate.js:353:35)
at localCompile (/Users/matt/staging/cabochon/packages/monolith/node_modules/ajv/lib/compile/index.js:88:22)
at Ajv.compile (/Users/matt/staging/cabochon/packages/monolith/node_modules/ajv/lib/compile/index.js:55:13)
at Ajv._compile (/Users/matt/staging/cabochon/packages/monolith/node_modules/ajv/lib/ajv.js:346:27)
at Ajv.validate (/Users/matt/staging/cabochon/packages/monolith/node_modules/ajv/lib/ajv.js:94:36)
at ReadStream.<anonymous> (/Users/matt/staging/cabochon/packages/monolith/test.js:14:19)
What results did you expect?
Successful validation of the schema.
Are you going to resolve the issue?
Yes, I have a PR ready to open.
I want to note that this issue was briefly touched on at the end of the conversation around https://github.com/epoberezkin/ajv/issues/486 as well.
Although the documentation does say that nullable should default to false, there is no indication that specifying "nullable": false explicitly should not be allowed. The exact wording is
"Allows sending a null value for the defined schema. Default value is false."
To draw contrast to this, the document describes the required field of a Parameter Object with the following wording:
"Determines whether this parameter is mandatory. If the parameter location is "path", this property is REQUIRED and its value MUST be true. Otherwise, the property MAY be included and its default value is false."
I guess I was looking at this page that only mentions true, but allowing any boolean makes more sense.
Yeah, that page is not very clear. FWIW, having nullable support out of box like this was a huge draw to AJV. Thanks! Definitely a bummer to see OpenAPI and JSON Schema represent the same thing in such different ways, but I do see the benefit in considering null to be a value or more accurately the absence of a value (OpenAPI) rather than a type (JSON Schema).
Null is a value. And each value should have type. Would you rather JSON Schema treat null as object as Javascript does? Nullable:true to allow null value makes no more sense to me than falseable: true to allow false value... As you can see I am not a fan of Swagger decision to implement not even a subset or superset of JSON Schema but some kind of “sideset” :)
I'm in agreement that it sucks for everyone to have the two standards diverge. I think a preference for one over the other is definitely a matter of opinion rather than correctness, though. My ultimate preference would be that libraries like this do not _need_ to make exceptions or customizations to support standards like OpenAPI. That said, given the two options (one supported by OpenAPI and the other supported by JSON Schema), I do prefer the way OpenAPI encodes nullability. The problem I see with your comparison to falseable is that falseable only makes sense in the context of one type: Boolean. In other words, it would not make sense to see
{
"type": "string",
"falseable": true
}
nullable, on the other hand, applies to all types (in the context of a standard where null is not itself a type, which is the case for OpenAPI).
I don't think I am really prepared to say "OpenAPI has this right and JSON Schema has it wrong" but I can see the merits of the approach taken by OpenAPI -- Again, this is ignoring the _really_ big disadvantage imposed by diverging from JSON Spec.
in the context of a standard where
nullis not itself a type, which is the case for OpenAPI
As I wrote null is value, so it has to have some type. JSON Schema defines a special type "null" that has only one value null. It's definitely less confusing than treating null as an object as JS does.
Swagger says that null is a value but it has no type, which is not a matter of opinion - it is nonsense that doesn't exist in any other major typed language. They could have easily said that "type" keyword allows either one type, or only two types if one of them is null, i.e. type: 'null' is ok and type: ['string', 'null'] is ok, but type: ['string', 'number'] is not ok. It would be a subset of JSON schema and all the tools would have worked with it as easily as with nullable.
All ajv does with nullable is replacing it with type: [XXX, 'null'] before anything else happens.
Instead they decided to be clever and created a weird situation when null is a value that has no type and you need a special keyword to allow it.
And if you look at JSON standard, it doesn't define types for null, true, false, but it separates null from objects. So from this point of view I am not quite sure why having type 'boolean' has any more or less sense than type 'null', or why falseable is any worse than nullable. /rant
My background is mostly in C, C++, Objective-C, and now Swift. In C (and other languages with similar reference semantics), null is not a type anymore than a pointer is a type. A pointer can store the location in memory of an object and null is the absence of a memory location. Objective-C does have a NSNull type, but as far as I know, that type exists for the sole purpose of storing null in places where there _does_ need to be a value (like Dictionaries) and for allowing nil (Objective-C's spelling of null) to be serialized. In this case, there is a difference between nil and NSNull, but the distinction is not important on the other side of serialization (i.e. to a different context that is deserializing something that was serialized from an Objective-C object. In Swift, nil (the null value) is just equivalent to the .none case of the Optional type (so null does not have it's own type here, either).
Bottom line is, if we are talking about a serialization standard being able to bridge the gap between languages with very different ideas of what "null" means, I think that introducing a nullable property that can apply to any object and introducing a null type that can be allowed alongside any other type make a similar amount of sense to me. At the very least, neither approach is the obvious winner in my book.
Again, I am not trying to argue that it is a good thing the two standards are different AND I am _very_ grateful that you've found a convenient way to bridge the gap for people like myself who are finding that there are not yet any really well established OpenAPI data validators out there to choose from.
Most helpful comment
Null is a value. And each value should have type. Would you rather JSON Schema treat null as object as Javascript does? Nullable:true to allow null value makes no more sense to me than falseable: true to allow false value... As you can see I am not a fan of Swagger decision to implement not even a subset or superset of JSON Schema but some kind of “sideset” :)