When using the validator middleware and specifying a schema with a type string, if the value is a number it will be coerced into a string and pass validation.
Illustrated below. The validator will mark the event as valid, and coerce the foo variable into a string. Despite the value actually being a number.
const schema = {
required: ['foo'],
properties: {
foo: {
type: 'string'
}
}
}
const handler = middy((event, context, cb) => {
console.log(typeof event.foo) // string???
}).use(validator({
inputSchema: schema
}))
handler({
foo: 123
}, {}, () => {})
I've written a test that covers this.
The problem looks to be from coerceTypes config in ajv.
As after removing that the test above passes, as do all of the other tests.
@lmammino @willfarrell
The default options for middy were chosen carefully to best suit the most common use cases for someone building an API. Query strings always pass in as strings, and having proper types is extremely useful. Your json schema clear states that you want a string, thus we coerce to a string.
You have two options:
a) change your json schema. ie "foo":{"type":"number"}
b) override the defaults with your own options to best fit your specific needs. See the validator docs
Sample json schema that would use coerceTypes:'array'.
{
"type": "object",
"properties": {
"multiValueQueryStringParameters": {
"type": "object",
"properties": {
"items": {
"type": "array",
"items": {
"type": "string"
},
"default": []
}
}
},
"queryStringParameters": {
"type": "object",
"properties": {
"foo": {
"type": "string"
}
}
},
"required": ["multiValueQueryStringParameters","queryStringParameters"]
}
Thanks @willfarrell.
I appreciate there is a need for coercion for parameters like queryStringParameters etc.
However does this not eliminate the purpose of json schema or the validator middleware on things like body?
If I add "foo":{"type":"string"}, I expect if I add a value that isn't a string it will fail validation?
{
"type": "object",
"properties": {
"body": {
"type": "object",
"properties": {
"name": {
"type": "string"
}
}
},
"required": ["body"]
}
I'd expect this to error?
{
"body": {
"name": 1234
}
}
If you expect your API the be ridgid then yes your example above should fail. However, that can lead to a poor user experience in production in many use cases. I'm not going to write out all the possible use cases here, that would take all day. There are enough of them to show the benefits of having coerce enabled far outway the benefits of having it disabled by default. For a pro user like yourself, you can easily override the default, and force developers interacting with your API to write more verbose code to ensure all data types are correct before sending.
A strategy I use on one project is to disable the coerce option on development, this catches any type errors during my automated testing. This ensures any new front end code that might have an error is flagged for review. But as I'm sure you know, it's impossible to test everything an end-user could possibly do. Because of that, having coerce enabled by default ensures that your API runs smoothly when it matters.
If you're worried about a performance hit, the corece code is only run after type check has failed. So if you're always sending the right type, you shouldn't expect a performance hit.
Hope this helps you and others get a better understanding of this decision. It all comes down to making developer lives easier, which is why we're all using middy in the first place.
Fair enough, thanks @willfarrell