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

@minimajs/multipart

v1.3.0

Published

Multipart/form-data handling with file upload support, validation, and schema integration

Readme

@minimajs/multipart

Efficient multipart form data handling for Minima.js with multiple consumption modes.

npm install @minimajs/multipart

Overview

The package provides three ways to consume multipart data:

| Module | Memory | Use Case | | ----------- | ----------- | --------------------------------- | | multipart | Buffered | Simple uploads, small files | | streaming | Lazy buffer | Large files with File API | | raw | Unbuffered | Advanced use cases, custom piping | | schema | Disk-backed | Validated uploads with Zod |

multipart (Buffered)

Standard approach that buffers files into memory as Web API File objects.

import { multipart, helpers } from "@minimajs/multipart";

// Get single file by field name
const avatar = await multipart.file("avatar");
await helpers.save(avatar, "./uploads");

// Get first file
const [fieldName, file] = await multipart.firstFile();

// Iterate all files
for await (const [field, file] of multipart.files()) {
  console.log(file.name, file.size);
}

// Get text fields only
const fields = await multipart.fields<{ name: string; email: string }>();

// Iterate all fields and files
for await (const [name, value] of multipart.body()) {
  if (helpers.isFile(value)) {
    await helpers.save(value, "./uploads");
  } else {
    console.log(name, value);
  }
}

streaming (Lazy Buffer)

Recommended for large files. Returns StreamFile - a File subclass that doesn't load into memory until accessed.

import { streaming } from "@minimajs/multipart";
import { pipeline } from "node:stream/promises";
import { createWriteStream } from "node:fs";

const file = await streaming.file("video");

// Access metadata without loading file
console.log(file.name, file.type);

// Stream directly to disk (no memory buffering)
await pipeline(file.stream(), createWriteStream("./uploads/video.mp4"));

// Or buffer on demand
const text = await file.text();
const buffer = await file.arrayBuffer();

StreamFile API

class StreamFile extends File {
  stream(): ReadableStream; // Get web stream (one-time)
  bytes(): Promise<Uint8Array>; // Buffer and return bytes
  text(): Promise<string>; // Buffer and decode as text
  arrayBuffer(): Promise<ArrayBuffer>;
  toReadable(): Readable | null; // Get Node.js stream
  toFile(): Promise<File>; // Convert to standard File
}

raw (Advanced)

Low-level access to busboy streams for advanced use cases. For most large file handling, prefer streaming instead.

import { raw, helpers } from "@minimajs/multipart";
import { pipeline } from "node:stream/promises";
import { createWriteStream } from "node:fs";

// Get raw file stream
const file = await raw.file("video");
if (file) {
  await pipeline(file.stream, createWriteStream(`/uploads/${file.filename}`));
}

// Iterate raw files
for await (const file of raw.files()) {
  await helpers.save(file, "/uploads");
}

// Iterate all (fields + files)
for await (const item of raw.body()) {
  if (helpers.isRawFile(item)) {
    await helpers.drain(item); // or save
  } else {
    console.log(item.fieldname, item.value);
  }
}

Raw Types

interface MultipartRawFile {
  fieldname: string;
  filename: string;
  mimeType: string;
  transferEncoding: string;
  stream: BusboyFileStream;
  [RAW_FILE]: true;
}

interface MultipartRawField {
  fieldname: string;
  value: string;
  [RAW_FIELD]: true;
}

helpers

Utility functions for file handling.

import { helpers } from "@minimajs/multipart";

// Save file to disk (works with File or raw stream)
const filename = await helpers.save(file, "/uploads");
const filename = await helpers.save(file, "/uploads", "custom-name.png");

// Type guards
helpers.isFile(value); // value is File
helpers.isRawFile(value); // value is MultipartRawFile
helpers.isRawField(value); // value is MultipartRawField

// Discard raw stream
await helpers.drain(rawFile);

// Generate random filename preserving extension
helpers.randomName("photo.jpg"); // "550e8400-e29b-41d4-a716-446655440000.jpg"

// Ensure directory exists
await helpers.ensurePath("/uploads", "images");

// Human readable file size
helpers.humanFileSize(1048576); // "1.0 MiB"

schema (Zod Validation)

Type-safe multipart parsing with Zod schema validation. Files are saved to disk as TempFile (memory-efficient).

import { z } from "zod";
import { createMultipart } from "@minimajs/multipart/schema";
import { helpers } from "@minimajs/multipart";

const getBody = createMultipart({
  name: z.string().min(1).max(100),
  avatar: z
    .file()
    .mime(["image/jpeg", "image/png"])
    .max(5 * 1024 * 1024),
});

// In your handler
const data = await getBody();
console.log(data.name); // string (type-safe)
await helpers.save(data.avatar, "./uploads");

Multiple Files

const getBody = createMultipart({
  title: z.string(),
  photos: z.array(z.file().max(10 * 1024 * 1024)).max(5), // Max 5 files, 10MB each
});

const data = await getBody();
for (const photo of data.photos) {
  await helpers.save(photo, "./uploads/photos");
}

TempFile API

Files are stored on disk and implement the Web File interface:

class TempFile extends File {
  path: string; // Temp file path on disk
  stream(): ReadableStream; // Web stream
  bytes(): Promise<Uint8Array>;
  text(): Promise<string>;
  arrayBuffer(): Promise<ArrayBuffer>;
  toReadable(): Readable; // Node.js stream
  toFile(): Promise<File>; // Load into memory
  destroy(): Promise<boolean>; // Delete temp file
}

Files are automatically cleaned up after the request via defer().

Validation Methods

z.file(); // Any file
z.file().max(1024 * 1024); // Max 1MB
z.file().min(100); // Min 100 bytes
z.file().mime(["image/png"]); // Specific MIME types
z.file().mime(["image/*"]); // MIME wildcards

Options

All functions accept MultipartOptions (busboy config):

await multipart.file("avatar", {
  limits: {
    fileSize: 10 * 1024 * 1024, // 10MB max per file
    files: 5, // Max 5 files
    fields: 20, // Max 20 text fields
  },
});

Full documentation available at minimajs.com/packages/multipart.

License

MIT