Skip to content

Multipart Parser Overview ​

When a browser submits a <form> with enctype="multipart/form-data", the request body is encoded in a format called multipart. Multipart encoding splits the body into parts separated by a boundary string. Each part carries its own headers (like Content-Disposition and Content-Type) followed by the part's body bytes. This is how file uploads travel over HTTP.

The multipart-parser package gives you a streaming parser for this format. It works in any JavaScript runtime -- Node.js, Deno, Bun, Cloudflare Workers, and browsers -- because it builds on web-standard APIs (ReadableStream, Uint8Array, Headers).

When to Use This Package ​

Most applications should reach for form-data-parser instead, which provides a higher-level API that returns a standard FormData object. Use multipart-parser directly when you need:

  • Fine-grained control over how individual parts are processed
  • To stream file parts directly to storage without buffering
  • Custom limits on header size, file size, or total request size
  • Access to raw part headers and bodies

Quick Example ​

ts
import {
  parseMultipartStream,
  isMultipartRequest,
  getMultipartBoundary,
} from 'remix/multipart-parser'

async function handleUpload(request: Request) {
  if (!isMultipartRequest(request)) {
    return new Response('Expected multipart form data', { status: 400 })
  }

  let boundary = getMultipartBoundary(request)!

  for await (let part of parseMultipartStream(request.body!, boundary)) {
    let disposition = part.headers.get('Content-Disposition')
    console.log(disposition) // 'form-data; name="avatar"; filename="photo.jpg"'
    console.log(part.body.byteLength)
  }

  return new Response('Upload received')
}

Key Concepts ​

  • Boundary -- A unique string embedded in the Content-Type header that separates parts in the multipart body. The parser needs this to know where one part ends and the next begins.
  • Part -- A single field or file in the multipart body. Each part has its own Headers object and a Uint8Array body.
  • Streaming -- parseMultipartStream yields parts one at a time via an async iterator, so you never need to hold the entire request body in memory.

Three Parsing Modes ​

FunctionUse Case
parseMultipart(body, boundary)Buffer all parts into an array. Simple but uses more memory.
parseMultipartStream(body, boundary)Async iterator that yields parts one at a time. Memory-efficient.
new MultipartParser(boundary)Low-level class with write/end for full control over buffering.

Safety Limits ​

The parser accepts options to enforce size and count limits:

ts
let parts = await parseMultipart(request.body!, boundary, {
  maxFileSize: 10 * 1024 * 1024,  // 10 MB per file
  maxParts: 20,                    // 20 fields/files max
  maxTotalSize: 50 * 1024 * 1024,  // 50 MB total
})

When a limit is exceeded, the parser throws a typed error (MaxFileSizeExceededError, MaxPartsExceededError, etc.) that you can catch and return an appropriate HTTP response.

Released under the MIT License.