Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,20 @@ fastify.post('/', async function (req, reply) {
})
```

Or to a route options when `attachFieldsToBody` is used.
```js
fastify.post('/', {
config: {
multipartOptions: {
limits: { fileSize: 1000 }
}
}
}, async function (req, reply) {
const buffer = req.body.file.toBuffer();
reply.send()
})
```

## Handle multiple file streams

```js
Expand Down
2 changes: 1 addition & 1 deletion index.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ function fastifyMultipart (fastify, options, done) {
return
}

for await (const part of req.parts()) {
for await (const part of req.parts(req.routeOptions.config.multipartOptions)) {
req.body = part.fields

if (part.file) {
Expand Down
124 changes: 124 additions & 0 deletions test/multipart-fileLimit.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -316,3 +316,127 @@ test('should NOT throw fileSize limitation error when throwFileSizeLimit is glob
t.assert.ifError(error)
}
})

test('should throw fileSize limitation error when used alongside attachFieldsToBody and set request config', async function (t) {
t.plan(1)

const fastify = Fastify()
t.after(() => fastify.close())

fastify.register(multipart, {
attachFieldsToBody: true
})

const randomFileBuffer = Buffer.alloc(2_000_000)
crypto.randomFillSync(randomFileBuffer)

fastify.post('/', {
config: {
multipartOptions: {
limits: {
fileSize: 1_000_000
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@grigoran

The file size limitation error isn’t caused by this configuration.
In this test, it should occur only when the request configuration is set, as described in the title.

Copy link
Author

@grigoran grigoran Oct 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@is2ei
Hello! The error is caused by this exact configuration; I've verified it locally. This configuration is also specified in the title, except for the multipart_options(multipartOptions) field name. If I've misunderstood something, please correct me

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@grigoran

Could you share how you verified it locally?
I commented out the config, but the test still passed.
It seems the error may not be caused by the config.

diff --git a/test/multipart-fileLimit.test.js b/test/multipart-fileLimit.test.js
index 6aed6a5..d6d4462 100644
--- a/test/multipart-fileLimit.test.js
+++ b/test/multipart-fileLimit.test.js
@@ -331,13 +331,13 @@ test('should throw fileSize limitation error when used alongside attachFieldsToB
   crypto.randomFillSync(randomFileBuffer)
 
   fastify.post('/', {
-    config: {
-      multipartOptions: {
-        limits: {
-          fileSize: 1_000_000
-        }
-      }
-    }
+    // config: {
+    //   multipartOptions: {
+    //     limits: {
+    //       fileSize: 1_000_000
+    //     }
+    //   }
+    // }
   }, async function (req, reply) {
     t.assert.fail('it should throw')

Copy link
Author

@grigoran grigoran Oct 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@is2ei
Got it. I tested it by changing the fileSize value to 2_500_000. Commenting out the configuration results in an error due to the default fileSize value, which is fastify.initialConfig.bodyLimit = 1_000_000.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If necessary, I can change the values ​​to make it clearer what is causing the error.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@grigoran

The test should be updated so that the error is caused by the config, as the message states:

should throw fileSize limitation error when used alongside attachFieldsToBody and set request config

}
}
}
}, async function (req, reply) {
t.assert.fail('it should throw')

reply.status(200).send()
})

await fastify.listen({ port: 0 })

// request
const form = new FormData()
const opts = {
hostname: '127.0.0.1',
port: fastify.server.address().port,
path: '/',
headers: form.getHeaders(),
method: 'POST'
}

const tmpFile = 'test/random-file'
fs.writeFileSync(tmpFile, randomFileBuffer)

const req = http.request(opts)
form.append('upload', fs.createReadStream(tmpFile))

form.pipe(req)

try {
const [res] = await once(req, 'response')
t.assert.equal(res.statusCode, 413)
res.resume()
await once(res, 'end')

fs.unlinkSync(tmpFile)
} catch (error) {
t.assert.ifError(error)
}
})

test('should not throw fileSize limitation error when used alongside attachFieldsToBody and set request config', async function (t) {
t.plan(4)

const fastify = Fastify()
t.after(() => fastify.close())

fastify.register(multipart, {
attachFieldsToBody: true
})

const randomFileBuffer = Buffer.alloc(900_000)
crypto.randomFillSync(randomFileBuffer)

fastify.post('/', {
config: {
multipartOptions: {
limits: {
fileSize: 1_000_000
}
}
}
}, async function (req, reply) {
t.assert.ok(req.isMultipart())

t.assert.deepStrictEqual(Object.keys(req.body), ['upload'])

const content = await req.body.upload.toBuffer()

t.assert.strictEqual(content.toString(), randomFileBuffer.toString())

reply.status(200).send()
})

await fastify.listen({ port: 0 })

// request
const form = new FormData()
const opts = {
hostname: '127.0.0.1',
port: fastify.server.address().port,
path: '/',
headers: form.getHeaders(),
method: 'POST'
}

const tmpFile = 'test/random-file'
fs.writeFileSync(tmpFile, randomFileBuffer)

const req = http.request(opts)
form.append('upload', fs.createReadStream(tmpFile))

form.pipe(req)

try {
const [res] = await once(req, 'response')
t.assert.equal(res.statusCode, 200)
res.resume()
await once(res, 'end')

fs.unlinkSync(tmpFile)
} catch (error) {
t.assert.ifError(error)
}
})
4 changes: 4 additions & 0 deletions types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ declare module 'fastify' {
interface FastifyInstance {
multipartErrors: MultipartErrors;
}

interface FastifyContextConfig {
multipartOptions?: Omit<BusboyConfig, 'headers'>
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a test for this type

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

}

type FastifyMultipartPlugin = FastifyPluginCallback<
Expand Down
16 changes: 16 additions & 0 deletions types/index.test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,22 @@ const runServer = async () => {
reply.send()
})

app.post('/upload/files', {
config: {
multipartOptions: {}
}
}, async function (req, reply) {
req.routeOptions.config.multipartOptions
expectType<Omit<BusboyConfig, 'headers'>>(req.routeOptions.config.multipartOptions)
reply.send()
})

app.post('/upload/files', async function (req, reply) {
req.routeOptions.config.multipartOptions
expectError<Omit<BusboyConfig, 'headers'>>(req.routeOptions.config?.multipartOptions)
reply.send()
})

await app.ready()
}

Expand Down
Loading