npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@primafuture/request-body-parser

v2.0.0

Published

A reusable streaming request-body parser for multipart, urlencoded, and raw HTTP bodies.

Readme

@primafuture/request-body-parser

A reusable streaming request-body parser for Node.js HTTP servers.

It supports:

  • multipart/form-data
  • application/x-www-form-urlencoded
  • raw passthrough mode for unsupported content types

The library is single-pass and stream-first:

  • file parts are exposed as streams
  • field parts are also exposed as streams
  • full field values and full files are not buffered in memory by the parser

Why this exists

This package keeps the proven low-level parsing ideas from busboy style parsers, but exposes a cleaner modern API:

  • parts are the main abstraction
  • both files and fields are streamed
  • completion and failure are represented with promises
  • unsupported content types do not throw, they fall back to raw mode

Install

Published package:

npm install @primafuture/request-body-parser

Inside this local package:

npm install

Build the library:

npm run build

Typecheck only:

npm run typecheck

This package builds to:

  • CommonJS: dist/index.cjs
  • ESM: dist/index.mjs
  • Type declarations: dist/index.d.ts

Quick Example

import { createServer } from 'node:http';

import { createIncomingRequestBodyParser } from '@primafuture/request-body-parser';

createServer((req, res) => {
  const parser = createIncomingRequestBodyParser(req);

  parser.onPart((part) => {
    let totalBytes = 0;

    part.stream.on('data', (chunk: Buffer) => {
      totalBytes += chunk.length;
    });

    part.completed.then(() => {
      if (part.kind === 'file') {
        console.log('file', part.name, part.filename, totalBytes);
      } else {
        console.log('field', part.name, part.charset, totalBytes);
      }
    }).catch((error) => {
      console.error('part failed', error);
    });
  });

  parser.completed.then(() => {
    res.statusCode = 200;
    res.end('ok\n');
  }).catch((error) => {
    res.statusCode = 400;
    res.end(`${String(error)}\n`);
  });
}).listen(8080);

Public API

createIncomingRequestBodyParser(req, options?)

Creates a parser for a single IncomingMessage.

import type { IncomingMessage } from 'node:http';

export function createIncomingRequestBodyParser(
  req: IncomingMessage,
  options?: Partial<IRequestBodyParserOptions>,
): IRequestBodyParser;

IRequestBodyParser

export interface IRequestBodyParser {
  bodyStream: IncomingMessage;
  completed: Promise<void>;
  contentType: string | null;
  failed: Promise<unknown>;
  mode: 'multipart' | 'raw' | 'urlencoded';
  onPart(listener: (part: TRequestBodyPart) => void): () => void;
}

Fields:

  • bodyStream: the original request body stream
  • contentType: normalized content type like multipart/form-data, or null
  • mode: parsing mode chosen from the incoming content type
  • completed: resolves when parser-level processing of the request body completes
  • failed: resolves with the error when parser-level processing fails
  • onPart(...): subscribes to streamed parts and returns an unsubscribe function

TRequestBodyPart

export type TRequestBodyPart = IRequestBodyFieldPart | IRequestBodyFilePart;

Shared fields:

  • kind: 'field' | 'file'
  • name: field name from the form
  • headers: normalized part headers
  • contentType: part content type or null
  • transferEncoding: part transfer encoding or null
  • stream: readable stream for this part body
  • completed: resolves when that specific readable part stream ends for the consumer

Field-specific fields:

  • charset

File-specific fields:

  • filename

Options

export interface IRequestBodyParserOptions {
  defaultCharset: string;
  defaultParameterCharset: string;
  maxFieldNameSize: number;
  maxHeaderPairs: number;
  maxPartHeadersSize: number;
  preserveFilePath: boolean;
}

Default values:

  • defaultCharset: 'utf-8'
  • defaultParameterCharset: 'latin1'
  • maxFieldNameSize: 100
  • maxHeaderPairs: 2000
  • maxPartHeadersSize: 16384
  • preserveFilePath: false

Supported Modes

Multipart

For multipart/form-data:

  • boundaries are parsed incrementally
  • each part is emitted as either a file part or a field part
  • part headers are parsed with explicit size and pair limits
  • file and field bodies are streamed without full buffering

File parts are currently identified by the presence of filename or filename* in Content-Disposition.

Urlencoded

For application/x-www-form-urlencoded:

  • names are decoded incrementally
  • values are emitted as streamed field bodies
  • percent-decoding across chunk boundaries is handled correctly
  • + is decoded as space

Raw

For unsupported or malformed content types:

  • mode === 'raw'
  • no parsed parts are emitted
  • the original request stream remains available as bodyStream

Important Behavior Notes

  • For parsed modes, consume emitted part streams. If you stop reading them, backpressure will correctly stall parsing.
  • parser.completed tracks parser/request completion. It does not wait for user code to fully drain every emitted part stream.
  • part.completed tracks the exposed readable stream for that individual part.
  • Destroying an emitted part stream cancels that part, rejects its completed promise, and lets the parser continue with later parts when the body format allows it.
  • failed is kept as a compatibility mirror for parser-level failures.
  • In raw mode, this library does not parse anything for you.
  • In parsed modes, do not separately consume bodyStream as if it were an independent second stream. The parser reads from the original request body once.

Smoke Server

A tiny local test server is included in examples/smoke.ts.

Start it:

npm run smoke

Or in watch mode:

npm run smoke:watch

The server listens on http://localhost:8338.

Multipart test

curl -F 'title=hello' -F 'upload=@./package.json' http://localhost:8338

Urlencoded test

curl \
  -H 'content-type: application/x-www-form-urlencoded' \
  --data 'name=alice&note=hello+world' \
  http://localhost:8338

The smoke server:

  • prints parser activity to the console
  • writes a short per-part summary into the HTTP response

Packaging

This package is prepared for scoped publishing as @primafuture/request-body-parser.

Published entrypoints:

  • require('@primafuture/request-body-parser') -> dist/index.cjs
  • import '@primafuture/request-body-parser' -> dist/index.mjs
  • TypeScript declarations -> dist/index.d.ts

Current Limitations

  • There is no built-in aggregation of field values or files. That is intentional; downstream code should decide how to collect, sample, cap, or store data.
  • There are no built-in multipart part count, file count, or size limits yet beyond multipart header limits and urlencoded field-name length limits.
  • The parser is built for Node.js streams and IncomingMessage; it is not a Web Streams API package.