Optimization guide · uploads
How do you optimize large uploads in Node.js?
Short answer
Do not solve a large upload by raising the maximum body size. Keep prefetch disabled, create ctx.bodyStream() before the first await, and send it to a downstream pipeline. Cap one stream with maxStreamBodySize, cap concurrent uploads, and budget the storage destination. swm-uws 0.7.3 stops socket reads on pause(), so a slow consumer creates backpressure instead of retaining the whole body in memory.
- Readable
- body stream
- 413
- size exceeded
- pause/resume
- flow control
Scope
This page fixes the @swarmmachina/swm-core 5.1.2 and @swarmmachina/swm-uws 0.7.3 contracts for Node.js 22/24. It does not measure the throughput of a particular disk, object store, or network, and it does not promise a process-wide memory limit.
Streaming-upload contract
| Layer | Explicit boundary | Pre-release check |
|---|---|---|
| Reader | Create ctx.bodyStream() synchronously before the first await and do not use another body reader or prefetch for that request. | Test an asynchronous before hook, reader conflict, and an early authorization refusal. |
| Size | http.maxStreamBodySize is the server ceiling; route and per-call maxSize can only narrow it. | Check Content-Length above the limit and a chunked upload that crosses it while reading. |
| Memory | Streaming is not charged to maxBodyBudget, but downstream queues, retained chunks, and concurrent uploads still consume memory. | Record RSS, queue depth, and active uploads in a separate load run. |
| Backpressure | When a consumer is not ready, native pause stops further socket reads; chunks already in the parser buffer can still reach a callback. | Slow the destination write, observe pause/resume, and verify that the process does not buffer the whole file. |
| Cancellation | When a handler stops reading the body, call stream.destroy() and finish the response under your own policy. | Cover an authorization refusal, client FIN, and connection reuse after drain. |
| Admission | One file limit does not cap the total of simultaneous files: define a separate concurrency limit and storage budget. | Start more concurrent uploads than the policy permits and prove that refusal is controlled. |
Choose from the upload profile
- 01
A small JSON or form payload
Keep a bounded body readerbody(), json(), or prefetch is simpler when materialization is inside a known limit and the application needs the complete value.
- 02
A file goes to disk or object storage
Use ctx.bodyStream()The pipeline receives chunks as they arrive and does not need to hold the complete file in JavaScript memory.
- 03
The downstream is temporarily slower than the network
Use pause/resume, but measure itBackpressure bounds socket reads, but it does not provide admission control or bound memory in every other part of the process.
What this does not promise
- A stream does not make memory use infinitesimal: the parser buffer, consumer-held chunks, and downstream queues remain budget items.
- maxStreamBodySize limits one request, not the total of concurrent uploads or the object-storage budget.
- Do not mix a streaming reader with body(), buffer(), text(), json(), or prefetch in one request.
- This page describes a contract, not a universal benchmark. Verify performance with your file sizes, disk, network, and concurrency.
Primary sources
Claims about the reader, limits, and pause/resume are pinned to the published package versions.