fastify-multipart
fastify-multipart copied to clipboard
The coerceTypes: 'array' doesn't coerce one parameter to a single element in array
Prerequisites
- [X] I have written a descriptive issue title
- [X] I have searched existing issues to ensure the bug has not already been reported
Fastify version
3.x.x
Plugin version
5.x.x
Node.js version
14.x
Operating system
Windows
Operating system version (i.e. 20.04, 11.3, 10)
10
Description
I have got this error when send a POST request.
{statusCode: 400, error: "Bad Request", message: "body.authors should be array"}
error: "Bad Request"
message: "body.authors should be array"
statusCode: 400
My schema
const body = {
type: "object",
properties: {
authors: {
type: "array",
items: {
type: "object"
}
}
}
Fastify configuration
server.register(fastifyMultipart, {
attachFieldsToBody: true,
sharedSchemaId: "#mySharedSchema",
})
const ajv = new Ajv({
removeAdditional: true,
useDefaults: true,
coerceTypes: "array",
nullable: true
})
fastify.setValidatorCompiler(({ schema }) => {
return ajv.compile(schema)
})
Steps to Reproduce
It is straightforward.
Expected Behavior
No response
Can you please include a reproducible example? It might be straightforward but it will greatly simplify fixing it. You could also send a PR, it would be highly welcomed.
Hi, I dunno how to create a reproducible example that is easy for everyone to see.
But I wrote a test, could you copy it to the test directory of fastify-multipart
to check this problem:
https://gist.github.com/meotimdihia/53299a9501843987907a369f445898ac
If I uncomment line 79 then the test will be passed.
I've taken a look at this and I think without a reproducible example it's not going to be possible to definitively conclude on this issue. To me it looks as though it could be to do with the way you are registering routes and the validation on your fastify server, as in the below example where username gets coerced into an array.
import Fastify from 'fastify'
import Ajv from 'ajv'
import fastifyMultipart from 'fastify-multipart'
function buildServer() {
const fastify = Fastify({
logger: {
prettyPrint: true,
},
})
const ajv = new Ajv({
removeAdditional: true,
useDefaults: true,
coerceTypes: 'array',
})
const opts = {
attachFieldsToBody: true,
sharedSchemaId: '#sharedLoginSchema',
}
fastify.register(fastifyMultipart, opts)
fastify.setValidatorCompiler(({ schema }) => ajv.compile(schema))
// works
fastify.register(async function (fs) {
fs.post(
'/login',
{
schema: {
body: {
type: 'object',
properties: {
username: {
type: 'array',
},
password: {
type: 'string',
},
},
},
},
},
async req => {
const { username, password } = req.body
return { username, password }
}
)
})
// doesn't work
fastify.post(
'/login2',
{
schema: { body: fastify.getSchema('login') },
schema: {
body: {
type: 'object',
properties: {
username: {
type: 'array',
},
password: {
type: 'string',
},
},
},
},
},
async req => {
const { username, password } = req.body
return { username, password }
}
)
fastify.log.info('Fastify is starting up!')
return fastify
}
const fastify = buildServer()
const start = async function () {
try {
await fastify.listen(3000)
} catch (err) {
fastify.log.error(err)
process.exit(1)
}
}
start()
If this is the cause of the confusion (and I agree it is not obvious that you might register the route like this) then perhaps the solution is simply to update the docs.
Also, from what I can tell, this isn't to do with fastify-multipart as the same behaviour is seen on all fastify route validation.
@andrewwood2 could you send a PR to update the docs?
I am having a similar (or same problem), where I define a property of my schema as a { type: 'array', items: fastify.getSchema('mySharedSchema')}'
. When I send only one file, it says that field expect an array. I am using "coerceTypes": Array
, so I expected that it would work without any workaround.
As a temporary fix I am doing a anyOf
schema with a single File
and a array FileArray
.
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.
Hi, I added the example: https://github.com/meotimdihia/fastify-multipart-bug-report-2
you'll get this error:

P/S: Please also re-open this issue: https://github.com/fastify/fastify-multipart/issues/313
Also bumped into this issue today trying to get a 1 or more files
field to work. It seems that ajv will only coerce scalar types as defined in https://github.com/ajv-validator/ajv/blob/master/lib/compile/validate/dataType.ts#L126
For multipart file uploads, dataType
is object
and thus not automatically coerced.
A potential fix would be to change the filesFromFields
function to somehow detect if the underlying schema wants array types. Replacing the schema with oneOf hinders the swagger ui for file upload.
I worked around this by creating a custom ajv plugin
import type Ajv from "ajv";
import { DataValidateFunction } from "ajv/dist/types";
export const ajvArrayCoercion = (ajv: Ajv) => {
ajv.addKeyword({
keyword: "coerceObjectArray",
modifying: true,
compile: (schema, parent) => {
delete parent.coerceObjectArray;
const validator: DataValidateFunction = (data, dataCxt) => {
if (!Array.isArray(data) && typeof data === "object") {
// @ts-expect-error No typings for package ajv
dataCxt.parentData[dataCxt.parentDataProperty] = [data];
}
return true;
};
return validator;
},
before: "array",
});
return ajv;
};
Which I then use as
multiFileField: {
allOf: [
{
coerceObjectArray: true,
},
{
type: "array",
items: {
isFile: true,
},
},
],
} as unknown as {
type: "array";
items: {
isFile: true;
};
}