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.