mrkeyoor.com_
Sun 20 Sept 11:43 UTC
npmUtilsupdated 20 Sept 2026

formidable review

Formidable 3.5.4 reads a Node `IncomingMessage` and parses multipart uploads, URL-encoded fields, JSON, or octet-stream bodies. Multipart files go to the operating system's temporary directory by default, though `fileWriteStreamHandler` can send each file into your own Writable stream. The parser enforces separate limits for file count, bytes per file, total file bytes, field count, and field bytes. It returns `[fields, files]` from `parse()`, with arrays for values in the 3.x API. The 3.5.4 release notes describe a switch to pnpm, fewer project dependencies, audit-related dependency fixes, and removal of an `os.machine` call tied to issue 994.

Verdict

Formidable 3.5.4 installed in 0.9 seconds with 7 packages, 3 MB on disk, and 0 audit findings in our sandbox, but its browser bundle failed and no TypeScript declarations shipped. Install it for controlled Node upload streams; choose framework middleware for `req.file` conventions or a Fetch-native parser at the edge.

We installed it

Lab card: what happened when we installed formidableScreenshot of formidable documentation
Install✓ · 0.9s7 packages on disk · 3 MB
ImportESM import works · require() works · ESM package with exports map
Browsern/acould not be bundled for the browser (Node-only code, most likely)
Typesno TypeScript types found
Known vulns00 critical · 0 high · 0 moderate · 0 low (npm audit)

Answers from our run

Does formidable install cleanly?

Yes. In a fresh container with an empty cache, npm install formidable finished in 0.9s, leaving 7 packages and 3 MB on disk. npm audit reported no known vulnerabilities.

Can formidable run in a browser?

Not directly: esbuild could not bundle it for the browser in our run, which normally means it depends on Node built-ins. Use it on the server, or find a browser-targeted alternative.

Does formidable work with both ESM and CommonJS?

Yes. Both import 'formidable' and require('formidable') worked in Node 22 in our run. The package is published as ESM with an exports map.

Does formidable include TypeScript types?

No type declarations were found in our install, so TypeScript users need their own declarations.

formidable or busboy: which should you use?

busboy: Use it for lower-level multipart events when you want to build field collection and storage yourself. Formidable 3.5.4 installed in 0.9 seconds with 7 packages, 3 MB on disk, and 0 audit findings in our sandbox, but its browser bundle failed and no TypeScript declarations shipped.

When should you not use formidable?

The runtime exposes only Fetch Request objects, as many edge platforms do. Formidable consumes Node's incoming request stream.

API stability3/5The 3.x factory, promise-returning `parse()`, event names, limit options, and plugin hooks remain in the current README, and 3.5.4 changes project dependencies plus one platform call rather than the request API. Migration still has a sharp boundary: 3.x uses arrays for field and file values where older applications often expect scalars. The README also points fresh projects toward an experimental `formidable-mini` that may inform a future v4.
Docs4/5The repository documents raw Node, Express, and Koa handlers, every main option with its default, parser plugins, lifecycle events, file objects, custom Write streams, and helpers for converting 3.x field arrays. It clearly warns about `ctx.req`, a missing upload directory, and old vulnerable majors. One contradiction remains visible: package metadata says Node 14 or newer, while the installation section tells users to run Node 20 or newer.
Maintenance3/5GitHub showed an unarchived repository pushed on August 6, 2026, with 7,178 stars and 45 open issues and pull requests. The v3.5.4 notes cover dependency reduction, audit fixes, the pnpm move, and an `os.machine` compatibility fix. Work continues, though the README says a few maintainers handle the project and describes the smaller Formidable Mini as unfinished groundwork rather than a stable successor.
Ecosystem4/5npm recorded 25,023,650 downloads in the latest completed week. Formidable accepts the raw Node request used beneath several web frameworks and lets storage adapters receive file bytes through ordinary Writable streams. That makes local disk and object-store integrations possible without a framework tie-in. Fetch-only runtimes need an adapter or another parser, Express users do not receive Multer-style request properties, and our inspection found no bundled types.

Use it if

  • A Node HTTP endpoint must receive large multipart files without buffering the entire request in memory.
  • Uploads need to flow into object storage through a Writable returned by `fileWriteStreamHandler`.
  • The route needs independent ceilings for each file, the combined file payload, field bytes, file count, and field count.
  • You want to enable only selected built-in parsers or attach a custom parser plugin to the form instance.
Skip it if

Setup reality

We installed formidable 3.5.4 in a fresh Node 22 Bookworm sandbox in 0.9 seconds. It left 7 packages using 3 MB, and npm audit found 0 known vulnerabilities. The package declares 3 direct dependencies, 0 peer dependencies, and 316 KB unpacked. It is marked as ESM with an exports map; both require() and ESM import worked. We found no bundled TypeScript declarations.

No login or service credential is part of Formidable itself. Storage is the first setup choice. The default uploadDir is the operating system's temporary directory. Create a custom directory before parsing because the README warns that files are silently discarded when that directory does not exist. Treat originalFilename and mimetype as untrusted client values, and generate your own destination name.

The request body can be consumed once. Keep other body parsers away from this route, pass ctx.req from Koa, and set limits before calling parse(). Version 3 defaults include 1,000 fields, 1,000 files, 200 MB per file, and a total file limit derived from maxFileSize; production endpoints usually need smaller values chosen for their workload. A false result from filter ignores a file without rejecting the whole form.

Our esbuild browser build failed because the entry depends on Node-only modules. The package metadata accepts Node 14 or newer, while the current README says Node 20 or newer, so deploy against the stricter documented floor. When a custom fileWriteStreamHandler replaces local storage, your code owns stream errors and destination completion. Parsing success alone does not prove that an object-store upload has finished.

Patterns

Await fields and files parse-multipart-request

import formidable from 'formidable';

const form = formidable({ uploadDir: './uploads', keepExtensions: true });
const [fields, files] = await form.parse(req);
console.log(fields.title?.[0], files.avatar?.[0]?.filepath);

Formidable 3 returns arrays under both fields and files, including a form that submitted one value.

Parse an Express request handle-express-route

app.post('/uploads', async (req, res, next) => {
  try {
    const [fields, files] = await formidable().parse(req);
    res.json({ fields, files });
  } catch (error) {
    next(error);
  }
});

The raw request stream is read once. Do not run JSON or URL-encoded body middleware first on this route.

Bound every upload dimension set-upload-limits

const form = formidable({
  maxFiles: 4,
  maxFileSize: 8 * 1024 * 1024,
  maxTotalFileSize: 20 * 1024 * 1024,
  maxFields: 40,
  maxFieldsSize: 512 * 1024,
});

The 3.x parser enforces per-file and total file-byte limits separately; set both for multi-file forms.

Map an oversize file to HTTP 413 classify-size-error

import { errors as formidableErrors } from 'formidable';

try {
  await form.parse(req);
} catch (error) {
  if (error.code === formidableErrors.biggerThanMaxFileSize) {
    res.writeHead(413).end('file too large');
    return;
  }
  throw error;
}

The exported error code for one file exceeding maxFileSize is 1016; Formidable assigns HTTP 413 to that failure.

Ignore unexpected file fields filter-file-parts

const form = formidable({
  filter(part) {
    return part.name === 'avatar' &&
      Boolean(part.mimetype?.startsWith('image/'));
  },
});

Returning false skips the part without failing parse(). The browser-supplied MIME string does not verify the file bytes.

Own the stored filename generate-safe-filename

import { randomUUID } from 'node:crypto';

const form = formidable({
  uploadDir: './uploads',
  keepExtensions: true,
  filename(_name, extension) {
    return `${randomUUID()}${extension}`;
  },
});

Create uploadDir before parsing. The README says an absent directory causes uploaded files to be discarded silently.

Supply a destination Writable stream-to-object-storage

const completions = [];
const form = formidable({
  fileWriteStreamHandler(file) {
    const target = openObjectUpload(file.newFilename);
    completions.push(target.done);
    return target.writable;
  },
});
await form.parse(req);
await Promise.all(completions);

With a custom write handler, wait for the destination's completion signal as well as Formidable's parse promise.

Calculate a SHA-256 digest hash-file-during-upload

const form = formidable({ hashAlgorithm: 'sha256' });
const [, files] = await form.parse(req);
const digest = files.document[0].hash;

The hash is updated while bytes stream in and is available on the completed file object.

Read server-side byte progress observe-server-progress

form.on('progress', (received, expected) => {
  if (expected) console.log(Math.round((received / expected) * 100));
  else console.log(received);
});

bytesExpected depends on a usable Content-Length header and can be null for a chunked request.

Clean files after processing remove-temporary-files

import { unlink } from 'node:fs/promises';

const [, files] = await form.parse(req);
try {
  await inspectUpload(files.document[0]);
} finally {
  const all = Object.values(files).flat();
  await Promise.all(all.map(file => unlink(file.filepath).catch(() => {})));
}

Formidable does not remove local upload files after application processing ends.

Use Koa's Node request parse-koa-request

router.post('/uploads', async (ctx) => {
  const [fields, files] = await formidable().parse(ctx.req);
  ctx.body = { fields, files };
});

Pass `ctx.req`, which is the Node IncomingMessage. `ctx.request` is Koa's wrapper object.

Allow multipart bodies only enable-multipart-parser

import formidable, { multipart } from 'formidable';

const form = formidable({ enabledPlugins: [multipart] });

At least 1 built-in plugin must remain enabled. This list rejects Formidable's JSON, query-string, and octet-stream parsing paths.

Alternatives

PackageRegistryPick it when
busboynpmUse it for lower-level multipart events when you want to build field collection and storage yourself.
multernpmUse it when Express middleware and `req.file` or `req.files` are the desired route interface.
multipartynpmUse it mainly to maintain code already written around its event and callback model.

More utils guides

lru-cache · ajv · type-fest · p-limit · find-up · js-yaml · the whole shelf →

How this guide is made: grounded in the library's documentation, release notes, changelog, and issue history, on a fixed rubric — not a hands-on install of every release. The 50 most-downloaded entries are additionally install-verified in clean containers. Corrections: contact the desk.