CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-busboy

A streaming parser for HTML form data for node.js

Pending
Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

Busboy

Busboy is a high-performance streaming parser for HTML form data in Node.js applications. It provides efficient parsing of multipart/form-data and application/x-www-form-urlencoded content types with configurable security limits and full event-based API support for handling file uploads and form fields.

Package Information

  • Package Name: busboy
  • Package Type: npm
  • Language: JavaScript
  • Installation: npm install busboy

Core Imports

const busboy = require("busboy");

For ES modules:

import busboy from "busboy";

Basic Usage

const http = require("http");
const busboy = require("busboy");

http.createServer((req, res) => {
  if (req.method === "POST") {
    const bb = busboy({ headers: req.headers });
    
    bb.on("file", (name, file, info) => {
      const { filename, encoding, mimeType } = info;
      console.log(`File [${name}]: filename: ${filename}, encoding: ${encoding}, mimeType: ${mimeType}`);
      
      file.on("data", (data) => {
        console.log(`File [${name}] got ${data.length} bytes`);
      }).on("close", () => {
        console.log(`File [${name}] done`);
      });
    });
    
    bb.on("field", (name, val, info) => {
      console.log(`Field [${name}]: value: ${val}`);
    });
    
    bb.on("close", () => {
      console.log("Done parsing form!");
      res.writeHead(303, { Connection: "close", Location: "/" });
      res.end();
    });
    
    req.pipe(bb);
  }
}).listen(8000);

Architecture

Busboy is built around a streaming parser architecture:

  • Factory Function: Single entry point that creates appropriate parser instances based on Content-Type
  • Parser Selection: Automatically detects and instantiates multipart or URL-encoded parsers
  • Event-Driven API: Uses Node.js EventEmitter pattern for handling parsed data
  • Streaming Processing: Memory-efficient processing through Node.js streams
  • Security Limits: Configurable limits to prevent abuse and excessive resource usage

Capabilities

Parser Creation

Creates a streaming parser instance based on the Content-Type header.

/**
 * Creates and returns a new Writable form parser stream
 * @param {BusboyConfig} config - Configuration object with headers and options
 * @returns {Writable} Parser stream (Multipart or URLEncoded)
 * @throws {Error} If Content-Type is missing or unsupported
 */
function busboy(config);

interface BusboyConfig {
  /** HTTP headers of the incoming request (must include 'content-type') */
  headers: { [key: string]: string };
  /** highWaterMark for parser stream */
  highWaterMark?: number;
  /** highWaterMark for individual file streams */
  fileHwm?: number;
  /** Default character set when not defined (default: 'utf8') */
  defCharset?: string;
  /** Default charset for multipart parameter values (default: undefined, no decoding) */
  defParamCharset?: string;
  /** Preserve paths in filenames for multipart (default: false) */
  preservePath?: boolean;
  /** Various limits on incoming data */
  limits?: BusboyLimits;
}

interface BusboyLimits {
  /** Max field name size in bytes (default: 100) */
  fieldNameSize?: number;
  /** Max field value size in bytes (default: 1048576) */
  fieldSize?: number;
  /** Max number of non-file fields (default: Infinity) */
  fields?: number;
  /** Max file size in bytes for multipart (default: Infinity) */
  fileSize?: number;
  /** Max number of file fields for multipart (default: Infinity) */
  files?: number;
  /** Max number of parts for multipart (default: Infinity) */
  parts?: number;
  /** Max number of header key-value pairs for multipart (default: 2000) */
  headerPairs?: number;
}

File Event Handling

Emitted for each file found in multipart/form-data requests.

/**
 * File event - emitted for each new file found
 * @param {string} name - Form field name
 * @param {Readable} stream - File data stream with possible 'truncated' property
 * @param {FileInfo} info - File metadata
 */
parser.on("file", (name, stream, info) => {});

interface FileInfo {
  /** File's filename (WARNING: validate before use) */
  filename?: string;
  /** File's Content-Transfer-Encoding value */
  encoding: string;
  /** File's Content-Type value */
  mimeType: string;
}

Usage Example:

bb.on("file", (name, file, info) => {
  const { filename, encoding, mimeType } = info;
  
  // Always consume the stream to prevent hanging
  file.on("data", (data) => {
    // Process file data chunk
    console.log(`Received ${data.length} bytes`);
  });
  
  file.on("limit", () => {
    console.log("File size limit reached, file truncated");
  });
  
  file.on("close", () => {
    console.log("File stream closed");
    // Check file.truncated property if needed
    if (file.truncated) {
      console.log("File was truncated due to size limit");
    }
  });
});

Field Event Handling

Emitted for each non-file field found in form data.

/**
 * Field event - emitted for each new non-file field found
 * @param {string} name - Form field name
 * @param {string} value - Field value
 * @param {FieldInfo} info - Field metadata
 */
parser.on("field", (name, value, info) => {});

interface FieldInfo {
  /** Whether field name was truncated due to limits */
  nameTruncated: boolean;
  /** Whether field value was truncated due to limits */
  valueTruncated: boolean;
  /** Field's Content-Transfer-Encoding value */
  encoding: string;
  /** Field's Content-Type value */
  mimeType: string;
}

Usage Example:

bb.on("field", (name, val, info) => {
  console.log(`Field [${name}]: ${val}`);
  
  if (info.nameTruncated) {
    console.log("Field name was truncated");
  }
  
  if (info.valueTruncated) {
    console.log("Field value was truncated");
  }
});

Limit Event Handling

Events emitted when configured limits are reached.

/**
 * Parts limit event - emitted when configured parts limit reached
 * No more 'file' or 'field' events will be emitted
 */
parser.on("partsLimit", () => {});

/**
 * Files limit event - emitted when configured files limit reached
 * No more 'file' events will be emitted
 */
parser.on("filesLimit", () => {});

/**
 * Fields limit event - emitted when configured fields limit reached
 * No more 'field' events will be emitted
 */
parser.on("fieldsLimit", () => {});

Usage Example:

const bb = busboy({ 
  headers: req.headers,
  limits: {
    files: 3,
    fields: 10,
    parts: 15
  }
});

bb.on("filesLimit", () => {
  console.log("Maximum number of files reached");
});

bb.on("fieldsLimit", () => {
  console.log("Maximum number of fields reached");
});

bb.on("partsLimit", () => {
  console.log("Maximum number of parts reached");
});

Stream Event Handling

Standard Node.js stream events for completing form processing.

/**
 * Close event - emitted when parsing is complete
 * All files and fields have been processed
 */
parser.on("close", () => {});

/**
 * Finish event - emitted when all data has been written to the parser
 * Occurs before close event
 */
parser.on("finish", () => {});

/**
 * Error event - emitted when parsing errors occur
 * @param {Error} error - Error object with details
 */
parser.on("error", (error) => {});

Usage Example:

bb.on("close", () => {
  console.log("Form parsing completed successfully");
  res.writeHead(200, { "Content-Type": "text/plain" });
  res.end("Upload complete!");
});

bb.on("error", (err) => {
  console.error("Parsing error:", err);
  res.writeHead(400, { "Content-Type": "text/plain" });
  res.end("Bad request");
});

// Pipe the request to busboy to start parsing
req.pipe(bb);

Stream Methods

Since busboy returns a Writable stream, it supports standard stream methods.

/**
 * Write data to the parser
 * @param {Buffer|string} chunk - Data chunk to parse
 * @param {string} encoding - String encoding (if chunk is string)
 * @param {Function} callback - Callback when write completes
 * @returns {boolean} Whether stream can accept more data
 */
parser.write(chunk, encoding, callback);

/**
 * Signal end of input data
 * @param {Buffer|string} chunk - Optional final chunk
 * @param {string} encoding - String encoding (if chunk is string)
 * @param {Function} callback - Callback when end completes
 */
parser.end(chunk, encoding, callback);

/**
 * Destroy the parser stream
 * @param {Error} error - Optional error to emit
 */
parser.destroy(error);

Error Handling

Busboy throws or emits errors in the following scenarios:

  • Missing Content-Type: When headers['content-type'] is missing or invalid
  • Unsupported Content-Type: When content type is not multipart/form-data or application/x-www-form-urlencoded
  • Missing Boundary: When multipart data lacks required boundary parameter
  • Malformed Data: When form data structure is invalid or corrupted
  • Unexpected End: When form data ends unexpectedly during parsing

Common Error Handling Pattern:

try {
  const bb = busboy({ headers: req.headers });
  
  bb.on("error", (err) => {
    console.error("Busboy parsing error:", err.message);
    if (!res.headersSent) {
      res.writeHead(400, { "Content-Type": "text/plain" });
      res.end("Invalid form data");
    }
  });
  
  req.pipe(bb);
} catch (err) {
  console.error("Busboy creation error:", err.message);
  res.writeHead(400, { "Content-Type": "text/plain" });
  res.end("Invalid request");
}

Advanced Usage Patterns

File Upload with Disk Storage

const fs = require("fs");
const path = require("path");
const { randomBytes } = require("crypto");

bb.on("file", (name, file, info) => {
  // Generate safe filename
  const ext = path.extname(info.filename || "");
  const filename = randomBytes(16).toString("hex") + ext;
  const saveTo = path.join("/uploads", filename);
  
  const writeStream = fs.createWriteStream(saveTo);
  file.pipe(writeStream);
  
  writeStream.on("close", () => {
    console.log(`File ${name} saved to ${saveTo}`);
  });
});

Memory Buffer Collection

const files = {};
const fields = {};

bb.on("file", (name, file, info) => {
  const chunks = [];
  
  file.on("data", (data) => {
    chunks.push(data);
  });
  
  file.on("close", () => {
    files[name] = {
      buffer: Buffer.concat(chunks),
      info: info
    };
  });
});

bb.on("field", (name, val, info) => {
  fields[name] = { value: val, info: info };
});

bb.on("close", () => {
  console.log("Collected files:", Object.keys(files));
  console.log("Collected fields:", Object.keys(fields));
});

Security Configuration

const bb = busboy({
  headers: req.headers,
  limits: {
    fieldNameSize: 100,       // Limit field name length
    fieldSize: 1024 * 1024,   // 1MB field value limit
    fields: 20,               // Max 20 fields
    fileSize: 10 * 1024 * 1024, // 10MB file limit
    files: 5,                 // Max 5 files
    parts: 25,                // Max 25 total parts
    headerPairs: 100          // Limit header pairs (multipart only)
  }
});
Workspace
tessl
Visibility
Public
Created
Last updated
Describes
npmpkg:npm/busboy@1.6.x
Publish Source
CLI
Badge
tessl/npm-busboy badge