multipart-parser

A fast, zero-dependency streaming multipart parser that works in Node, Bun, Deno, Cloudflare Workers, and the browser.

Library
npm
v0.10.1
974stars
MIT License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
43/100Fair
Development Activity0
Maintenance48
Community40
Maturity44
Momentum40

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
80/100Excellent
Architecture88
Code Quality82
Innovation74
Learning Curve75

multipart-parser is a streaming parser for multipart/* HTTP messages (most commonly multipart/form-data file uploads) that runs unmodified across every major JavaScript runtime. Instead of buffering an entire request body before parsing, it yields each MultipartPart as its boundary is found, keeping memory usage flat even for large or many-file uploads.

It is built directly on the standard Fetch API (Request, ReadableStream) rather than any single runtime’s native stream types, with an optional Node.js-specific entry point for servers that still work with http.IncomingMessage and stream.Readable. Built-in maxFileSize and maxHeaderSize limits, paired with a typed hierarchy of parse errors, make it straightforward to reject oversized or malformed uploads before they exhaust server memory.

What You Get

  • High-level parseMultipartRequest() for parsing multipart/form-data directly off a Fetch API Request
  • Low-level parseMultipart() / parseMultipartStream() generators for buffers or streams that aren’t tied to an HTTP request
  • A dedicated /node entry point with bindings for http.IncomingMessage and stream.Readable
  • Built-in maxFileSize / maxHeaderSize limits enforced via typed MaxFileSizeExceededError / MaxHeaderSizeExceededError
  • A rich MultipartPart API exposing arrayBuffer, bytes, text, size, filename, mediaType, and name

Common Use Cases

  • File upload endpoints - API/server developers handling multipart/form-data POST requests stream large uploads without buffering the entire request body in memory.
  • Cross-runtime serverless functions - Teams deploying the same handler to Cloudflare Workers, Deno Deploy, and Node use one multipart implementation that behaves identically everywhere.
  • Replacing busboy in performance-sensitive servers - Backend teams benchmarking upload throughput swap in multipart-parser for its measured speed advantage over busboy and comparable libraries.
  • Building custom form-data proxies or gateways - Infrastructure engineers parsing and re-emitting multipart bodies (request inspection, virus scanning, etc.) use the low-level parseMultipart() generator directly on raw byte streams.

Under The Hood

Architecture The package follows a clean single-purpose module structure: MultipartParser (src/lib/multipart.ts) implements a hand-written finite-state machine (states: Start, AfterBoundary, Header, Body, Done) that consumes Uint8Array chunks incrementally via write(), buffering partial reads and transitioning state inline inside a single loop. This avoids pulling in a full push-based stream library while still supporting both a low-level generator API (parseMultipart() / parseMultipartStream()) and a higher-level parseMultipartRequest() (src/lib/multipart-request.ts) that wraps it around a Fetch API Request. The parser delegates boundary detection to buffer-search.ts, a self-contained Boyer-Moore-Horspool-style substring search (with a Buffer.indexOf fast path on Node) plus a dedicated partial-tail-boundary matcher for boundaries split across chunk edges — a clearly separated concern from state management. MultipartPart is a thin, lazily-computed value object over raw byte chunks that delegates header parsing to the sibling @mjackson/headers package rather than reimplementing HTTP header parsing. Node-specific glue is isolated behind its own entry point so runtimes without IncomingMessage/Readable never pay for that code path. Changing the core state machine touches only one file; the parts, boundary search, and request adapters stay decoupled from it.

Tech Stack Pure TypeScript targeting the Web Streams/Fetch API (ReadableStream<Uint8Array>, Request) as its primary interface, with an explicit Node.js compatibility layer built on http.IncomingMessage / stream.Readable / Buffer. It has zero runtime dependencies aside from the workspace-local @mjackson/headers package for header parsing. Builds run through esbuild, producing dual ESM/CJS output for both a runtime-neutral bundle and a Node-specific one, with .d.ts files generated via tsc. Tests run on Node’s built-in test runner rather than a third-party framework, and a bench/ directory runs the same benchmark across Node, Bun, and Deno against busboy, @fastify/busboy, and multipasta. The package lives inside a pnpm workspace monorepo and publishes to both npm and JSR. CI runs install, build, and test on GitHub Actions against Node 24.

Code Quality Tests are substantial and specific rather than superficial: one suite exercises parseMultipartRequest end-to-end against constructed Request objects, covering boundary edge cases, size-limit errors, and content-type validation; another covers the Node-specific adapter; a third unit-tests the boundary-search primitive directly. Error handling is explicit and typed — a MultipartParseError base class with specific subclasses (MaxHeaderSizeExceededError, MaxFileSizeExceededError) rather than generic thrown strings or swallowed exceptions — and the state machine throws descriptive errors for malformed input (missing initial boundary, unterminated stream, data written after the stream ends). Naming is consistent throughout, private class fields use real #-syntax encapsulation, and public APIs carry full type annotations and JSDoc comments. No dedicated lint step was found scoped to this package (it likely inherits shared monorepo tooling), and CI runs build and test but not a separate lint job.

What Makes It Unique Its differentiation sits in low-level performance engineering rather than an unfamiliar API shape — the top-level functions resemble other form-data parsers. What stands out is the custom Boyer-Moore-Horspool-derived boundary search with a dedicated partial-tail matcher, which lets the parser correctly detect a boundary spanning two chunks without ever buffering the full message, keeping it a genuine streaming generator that yields parts as they complete. It also swaps in Buffer.indexOf as a fast path on Node while falling back to the hand-rolled search on Bun, Deno, and browsers — a deliberate per-runtime performance choice backed by published benchmark numbers showing it consistently outperforming busboy and matching purpose-built alternatives. Building on the standard Fetch Request/ReadableStream interfaces as the primary API, with Node support as an explicit secondary adapter, is the reverse of how most existing multipart libraries in this space are structured.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search