busboy
A fast, streaming parser for multipart/form-data and URL-encoded HTTP request bodies in Node.js.
Repository Health
Technical Analysis
Busboy is a low-level Node.js library for parsing incoming HTTP request bodies encoded as multipart/form-data or application/x-www-form-urlencoded. Rather than buffering an entire upload into memory, it processes the raw request stream incrementally, emitting file and field events as soon as each part’s boundary and headers are parsed, so large file uploads can be piped straight to disk, cloud storage, or another stream without ever holding the whole payload in RAM.
It ships with a single runtime dependency (streamsearch, for boundary detection) and implements the multipart parsing state machine by hand, including configurable limits for field/file sizes, counts, and header sizes. Because it exposes a plain Writable stream with a small, well-known event API, busboy has become the parsing engine underneath many popular upload middlewares, including Express’s multer and Fastify’s @fastify/multipart, rather than something most applications reach for directly.
What You Get
- Streaming multipart/form-data parsing that emits Readable file streams instead of buffering uploads in memory
- Built-in URL-encoded (application/x-www-form-urlencoded) body parsing alongside multipart support
- Configurable limits for field size, file size, field/file/part counts, and header size to guard against abusive uploads
- Character-set aware field and filename decoding, including RFC 5987 extended parameter values
- A minimal event API (file, field, close, partsLimit, filesLimit, fieldsLimit) with a single runtime dependency
Common Use Cases
- Parsing file uploads in a raw Node.js HTTP server without pulling in a full web framework
- Powering higher-level upload middleware such as Express’s multer or Fastify’s @fastify/multipart
- Streaming large file uploads directly to disk, S3, or another destination as they arrive, instead of buffering them
- Extracting form field values from multipart or URL-encoded POST bodies in custom API servers
Under The Hood
Architecture lib/index.js dispatches based on the request’s Content-Type header to one of two Writable stream subclasses under lib/types/ (multipart.js, urlencoded.js), selected through a small static-detect()-based registry. Multipart.js implements a hand-written incremental state machine: a HeaderParser class parses each part’s header block byte-by-byte with an explicit state enum (name / pre-OWS / value), while a StreamSearch instance (from the streamsearch dependency) locates MIME boundaries and drives a callback that threads a matchPostBoundary state through boundary, header, and body transitions without ever buffering a whole part. Each detected file part gets its own FileStream (a Readable) whose backpressure is wired directly into the outer Writable’s _write callback, and the parser tracks how many file streams are still open so the final callback only fires once every file has finished. Data flow is single-pass and event-driven: one incoming Writable stream fans out into per-field and per-file emissions plus child Readable streams. Because boundary detection, header parsing, and backpressure all share mutable counters (parts, fields, files, matchPostBoundary), changing the core parsing loop would require re-verifying the whole state machine rather than one isolated layer.
Tech Stack package.json declares exactly one runtime dependency, streamsearch (^1.1.0), with no transpilation step — pure CommonJS targeting Node >=10.16.0. Dev dependencies are limited to @mscdex/eslint-config and eslint@7 for a lint / lint:fix script pair. There is no build step (main points straight at ./lib/index.js), no TypeScript, and no bundler. Tests run through a hand-rolled runner (test/test.js) that spawns each test-*.js file as a child process via Node’s built-in child_process.spawnSync and asserts with Node’s core assert module rather than a framework like Mocha or Jest. Benchmark scripts live under bench/ as standalone files. This is close to the minimum possible dependency footprint for a Node library — a deliberate choice for a low-level module that sits underneath many other packages.
Code Quality Tests exist and are substantial (test-types-multipart.js alone runs over a thousand lines, alongside dedicated files for URL-encoded bodies, multipart charsets, and stream-pause behavior), all using Node’s built-in assert module through the custom sequential runner — no coverage tooling or CI configuration was visible in the shallow clone, though. Error handling is explicit throughout the parser: malformed headers, missing content types, missing boundaries, and truncated streams all produce named Error objects (e.g. ‘Malformed part header’, ‘Unexpected end of form’) rather than failing silently. There is no TypeScript and no type annotations, but the code substitutes careful, defensive bounds and state checking (explicit byte-code comparisons such as code !== 58 for a colon) for type safety. ESLint is configured via @mscdex/eslint-config, and naming is terse but internally consistent (hparser, bparser, ssCb) with the low-level, performance-oriented style used throughout.
API Design The public API is a single default export function, busboy(config), returning a Node.js Writable stream — instantly familiar to any developer already comfortable with req.pipe(…): req.pipe(busboy({ headers: req.headers })). Event names (file, field, close, partsLimit, filesLimit, fieldsLimit) are self-descriptive, and every field/file callback receives a consistent info object carrying encoding and mimeType metadata. Configuration is a flat, well-documented limits object with sensible defaults (Infinity for counts, 1MB for field size) rather than an options-builder or subclassing requirement, and zero configuration is needed beyond passing headers to get a working parser. This low-ceremony surface is likely why busboy underlies form-data parsing in Express’s multer, Fastify’s @fastify/multipart, and other higher-level libraries rather than being reimplemented by each. It isn’t conceptually novel — it implements the well-known multipart/form-data spec (RFC 7578) — but its zero-dependency, backpressure-aware streaming implementation is more careful about memory than many alternatives that buffer entire files before exposing them.
Used by 9 apps in this directory
Blinko
Knowledge Management · Note Taking
A self-hosted, AI-powered card note-taking tool that lets you capture fleeting thoughts instantly and retrieve them with natural language search.
Directus
CMS · Low Code Platforms
Connect any SQL database and get instant REST and GraphQL APIs, a visual management Studio, and a native MCP server for AI agents — free for most organizations.
Ghost
CMS · Blogging
Open source headless Node.js CMS for professional publishing, paid memberships, and newsletters with a fully owned audience.
GitNexus
Developer Tools · AI Code Assistants
Index any codebase into an interactive knowledge graph and give your AI agents deep architectural context via MCP — with zero servers required.
Papra
Bookmarks Archiving
Self-hosted document archiving with email ingestion, OCR full-text search, and pluggable storage — store once, find anything.
Payload CMS
Developer Tools · Blogging · CMS
The open-source, Next.js-native headless CMS that lives inside your /app folder and gives you a full TypeScript backend instantly.
Puter
File Storage · Developer Tools
A self-hostable, web-based internet OS with desktop GUI, cloud storage, AI drivers, and a developer SDK — all running in your browser.
Rocket.Chat
Team Chat
The secure, self-hosted team communications platform for organizations that cannot compromise on data sovereignty.
TinaCMS
CMS
An open-source, Git-backed headless CMS that gives editors a live visual editing UI over Markdown, MDX, JSON, and YAML content while developers keep everything in version control.