File Upload
Problem
Section titled “Problem”You need to accept file uploads via multipart/form-data, enforce size
limits, and access file content (name, filename, body buffer) from within
your execute handler.
Solution
Section titled “Solution”ergo’s body() middleware handles multipart parsing automatically. When
the request Content-Type is multipart/form-data, acc.body.parsed
contains an array of part objects — one per form field or file.
Single File Upload
Section titled “Single File Upload”import { compose, body } from "@centralping/ergo";
const pipeline = compose( {fn: body(), setPath: "body"}, async (req, res, acc) => { const [file] = acc.body.parsed; await saveFile(file.filename, file.body); return { response: { statusCode: 201, body: { filename: file.filename, size: file.body.length }, }, }; },);router.post("/uploads", { body: { limit: 5 * 1024 * 1024 }, execute: async (req, res, acc) => { const [file] = acc.body.parsed; await saveFile(file.filename, file.body); return { response: { statusCode: 201, body: { filename: file.filename, size: file.body.length }, }, }; },});Multiple File Upload
Section titled “Multiple File Upload”import { compose, body } from "@centralping/ergo";
const pipeline = compose( {fn: body({ limit: 10 * 1024 * 1024 }), setPath: "body"}, async (req, res, acc) => { const files = acc.body.parsed.filter((part) => part.filename); const results = await Promise.all( files.map((file) => saveFile(file.filename, file.body)), ); return { response: { statusCode: 201, body: { uploaded: results.length }, }, }; },);router.post("/uploads/batch", { body: { limit: 10 * 1024 * 1024 }, execute: async (req, res, acc) => { const files = acc.body.parsed.filter((part) => part.filename); const results = await Promise.all( files.map((file) => saveFile(file.filename, file.body)), ); return { response: { statusCode: 201, body: { uploaded: results.length }, }, }; },});Accessing Form Fields Alongside Files
Section titled “Accessing Form Fields Alongside Files”import { compose, body } from "@centralping/ergo";
const pipeline = compose( {fn: body(), setPath: "body"}, async (req, res, acc) => { const parts = acc.body.parsed; const description = parts.find((p) => p.name === "description"); const file = parts.find((p) => p.filename); return { response: { statusCode: 201, body: { description: description?.body.toString(), filename: file?.filename, }, }, }; },);router.post("/documents", { body: true, execute: async (req, res, acc) => { const parts = acc.body.parsed; const description = parts.find((p) => p.name === "description"); const file = parts.find((p) => p.filename); return { response: { statusCode: 201, body: { description: description?.body.toString(), filename: file?.filename, }, }, }; },});Explanation
Section titled “Explanation”Multipart Parsed Shape
Section titled “Multipart Parsed Shape”When the body middleware parses a multipart/form-data request,
acc.body.parsed is an array of part objects:
| Property | Type | Description |
|---|---|---|
headers | object | RFC 7578 §4.8 headers (content-disposition, content-type, content-transfer-encoding) |
name | string | The form field name from Content-Disposition |
filename | string | undefined | The original filename (present only for file fields) |
body | Buffer | The raw binary content of the part |
File fields have a filename property; regular form fields do not. Use
this distinction to separate files from text fields in the parts array.
Size Limit Configuration
Section titled “Size Limit Configuration”| Option | Default | Description |
|---|---|---|
limit | 1048576 (1 MiB) | Maximum total body size in bytes (compressed) |
decompressedLimit | min(10 × limit, 10 MiB) | Maximum decompressed body size (bomb protection) |
The limit applies to the entire multipart body — all parts combined,
not per-part. For endpoints accepting large file uploads, increase the
limit explicitly:
body({ limit: 20 * 1024 * 1024 }); // 20 MiBContent Type Detection
Section titled “Content Type Detection”The body middleware accepts multipart/form-data by default (included
in the default types array). No additional configuration is needed to
enable multipart parsing — it works out of the box alongside JSON and
form-urlencoded bodies.
Part Count Limit
Section titled “Part Count Limit”The multipart parser enforces a maximum of 100 parts per request by default, preventing abuse from requests with thousands of empty parts. This limit is internal to the parser and does not require configuration.