fastify-multipart
fastify-multipart copied to clipboard
multipart does not work with validation and fastify-swagger
🐛 Bug Report
When used with validation and fastify-swagger with the same code from the example the file object is empty.
When validation is disabled, the upload works as intended.
To Reproduce
Register fastify-multipart and fastify-swagger, add a POST route with schema as in the example and try to send a multipart upload with the same name as defined in the schema.
Expected behavior
Needs to work same as without validation.
Your Environment
- node version: 12
- fastify version: 3.7.0
- os: Windows
Can you please provide a full example to reproduce? I'm not sure where the problem is and it will be very helpful. Essentially a server and a client calling it on the same javascript file / or maybe a repo. Essentially something that we can run that clearly show the failure.
@mcollina sure, sorry for making this harder.
Here's a package.json:
{
"name": "fastify-bug",
"version": "0.0.1",
"description": "",
"main": "index.js",
"author": "Max <[email protected]>",
"dependencies": {
"fastify": "^3.7.0",
"fastify-multipart": "^3.2.1",
"fastify-swagger": "^3.4.0",
"pino-pretty": "^4.3.0"
}
}
Here's a full example of how it won't work:
const fastify = require( 'fastify' );
const swagger = require( 'fastify-swagger' );
const app = fastify( {
logger: {
prettyPrint: true
},
ajv: {
customOptions: {
jsonPointers: true,
allErrors: true
}
}
} );
app.register( require( 'fastify-multipart' ), {
attachFieldsToBody: true,
sharedSchemaId: '#mySharedSchema'
} )
app.register( swagger, {
routePrefix: '/docs',
swagger: {
info: {
title: 'Sample',
description: 'Sample',
version: '0.0.1'
},
host: 'localhost:3333',
schemes: ['http'],
securityDefinitions: {
bearerAuth: {
type: 'apiKey',
name: 'Authorization',
in: 'header',
description: 'Format: "Bearer $TOKEN"'
}
},
consumes: ['application/json','multipart/form-data'],
produces: ['application/json'],
tags: [
{
name: 'Files',
description: 'Upload, delete, get signed url'
}
]
},
exposeRoute: true
} );
app.post( '/upload', {
schema: {
description: 'Upload a File, the field name should be "file"',
tags: [ 'Files' ],
summary: 'Upload',
consumes: ['multipart/form-data'],
body: {
type: 'object',
required: ['file'],
properties: {
file: { $ref: '#mySharedSchema' }
}
},
response: {
201: {
description: 'Upload OK',
type: 'object'
},
400: {
description: 'Bad Request',
type: 'object'
}
}
}
}, async function ( request, response ) {
try {
const data = await request.file();
console.log( data );
response.code( 201 ).send();
} catch ( error ) {
console.error( error );
response.code( 400 ).send();
}
} );
app.listen( 3333, '0.0.0.0', function ( err, address ) {
if ( err ) {
app.log.error( err );
process.exit( 1 );
}
app.log.info( 'server listening on ' + address );
} );
If we comment out body in schema and attachFieldsToBody: true in the multipart regitration it will work without a problem.
Also there is some warning: (node:11548) ExperimentalWarning: The fs.promises API is experimental.
Here's the working example:
const fastify = require( 'fastify' );
const swagger = require( 'fastify-swagger' );
const app = fastify( {
logger: {
prettyPrint: true
},
ajv: {
customOptions: {
jsonPointers: true,
allErrors: true
}
}
} );
app.register( require( 'fastify-multipart' ), {
//attachFieldsToBody: true,
sharedSchemaId: '#mySharedSchema'
} )
app.register( swagger, {
routePrefix: '/docs',
swagger: {
info: {
title: 'Sample',
description: 'Sample',
version: '0.0.1'
},
host: 'localhost:3333',
schemes: ['http'],
securityDefinitions: {
bearerAuth: {
type: 'apiKey',
name: 'Authorization',
in: 'header',
description: 'Format: "Bearer $TOKEN"'
}
},
consumes: ['application/json','multipart/form-data'],
produces: ['application/json'],
tags: [
{
name: 'Files',
description: 'Upload, delete, get signed url'
}
]
},
exposeRoute: true
} );
app.post( '/upload', {
schema: {
description: 'Upload a File, the field name should be "file"',
tags: [ 'Files' ],
summary: 'Upload',
consumes: ['multipart/form-data'],
/*body: {
type: 'object',
required: ['file'],
properties: {
file: { $ref: '#mySharedSchema' }
}
},*/
response: {
201: {
description: 'Upload OK',
type: 'object'
},
400: {
description: 'Bad Request',
type: 'object'
}
}
}
}, async function ( request, response ) {
try {
const data = await request.file();
console.log( data );
response.code( 201 ).send();
} catch ( error ) {
console.error( error );
response.code( 400 ).send();
}
} );
app.listen( 3333, '0.0.0.0', function ( err, address ) {
if ( err ) {
app.log.error( err );
process.exit( 1 );
}
app.log.info( 'server listening on ' + address );
} );
(I've formatted your example)
(I've formatted your example)
Me too 👍
What error are you getting?
@mcollina no error at all, just the file object is undefined if the validation is set.
attachFieldsToBody: true seems to be causing this result.
Note that your code is not correct. Check out https://github.com/fastify/fastify-multipart#parse-all-fields-and-assign-them-to-the-body for the attachFieldsToBody
option. You can use that together with await request.file()
, we should throw an error if those are used together.
@mcollina I see, so it would be helpful if we can use request.file()
and have validation in the same time, instead of relying on buffers only to have validation to work properly.
Thank you!
I see, so it would be helpful if we can use request.file() and have validation in the same time, instead of relying on buffers only to have validation to work properly.
Validation happens at a specific phase of a the request lifecycle, i.e. before the handler is executed. The way request.file()
works is to have the server receive the file inside the handler execution, after validation was completed.
Maybe it's possible to perform it with the files stored on disk... wdyt @StarpTech?
Sorry for the late response. I have no time to invest.
No quite the same example but i made it work using this api spec: https://github.com/keycap-archivist/app/blob/master/src/assets/v2-spec.yaml#L81-L99
using this configuration: https://github.com/keycap-archivist/app/blob/master/src/app.ts#L39
@xplodeart can you test those specs?
@zekth what about a dynamic example? Unfortunately requestBody
doesn't seem to be allowed when setting a inline schema in app.ts (not an external file like you do).
@zekth what about a dynamic example? Unfortunately
requestBody
doesn't seem to be allowed when setting a inline schema in app.ts (not an external file like you do).
requestBody
is an OpenAPI naming, which is mapped in body
in fastify schemas. I can try to put a dynamic example up when i'll have time.
I have a same issue.
Evnironment
- fastify 4.5.3
- @fastify/multipart 7.1.2
- @fastify/swagger 7.5.1
Problem
fastify/multipart cannot create fileupload widget on swagger.io document
Reproducable Repo
- https://github.com/imjuni/maeum/blob/e8e94a8f6d80181281a863abd38a3a5ac7916c25/src/server/server.ts#L43
execute npm scripts
npm run dev
and open urlhttp://localhost:7878/swagger.io
in your browser
Clue
- Cannot found schemaId in @fastify/multipart() issue because prefix character '#'. I think that need fix documentation.
- I think people want to myFile1 (swagger file
upload widget disply), but raise validation error display because myFile1(include me) set type
string
. but swagger ui uploadbuffer
type.
Something wrong in my code?
Yes, this is exactly what I need! 🥹
Problem
fastify/multipart cannot create fileupload widget on swagger.io document
I have a same issue.
Evnironment
- fastify 4.5.3
- @fastify/multipart 7.1.2
- @fastify/swagger 7.5.1
Problem
fastify/multipart cannot create fileupload widget on swagger.io document
Reproducable Repo
- https://github.com/imjuni/maeum/blob/e8e94a8f6d80181281a863abd38a3a5ac7916c25/src/server/server.ts#L43 execute npm scripts
npm run dev
and open urlhttp://localhost:7878/swagger.io
in your browserClue
- Cannot found schemaId in @fastify/multipart() issue because prefix character '#'. I think that need fix documentation.
- I think people want to myFile1 (swagger file upload widget disply), but raise validation error display because myFile1(include me) set type
string
. but swagger ui uploadbuffer
type.Something wrong in my code?
I'm facing this exact issue. Is there any solution yet?
I manage to make it work like this
register multipart:
fastify.register(multipart);
schema.ts
export const createAppIntegrationSchema = {
tags: ["integrations"],
consumes: ["multipart/form-data"],
body: {
type: "object",
properties: {
name: { type: "string" },
description: { type: "string" },
icon_image: {
type: "file",
},
demo_url: { type: "string" },
},
},
response: {
201: {
description: "Successful response",
type: "object",
properties: {
created_at: { type: "string" },
updated_at: { type: "string" },
name: { type: "string" },
description: { type: "string" },
icon_image: { type: "string" },
demo_url: { type: "string" },
is_enabled: { type: "boolean" },
},
},
401: {
type: "object",
properties: {
status: { type: "number", default: 401 },
message: { type: "string" },
},
},
500: {
description: "Error response",
type: "object",
properties: {
status: { type: "number", default: 500 },
message: { type: "string" },
},
},
},
};
and in your handler use it like this:
const data = await request.file();
Could you send a PR to document this?