formidable vs express-fileupload vs busboy vs connect-busboy vs multer
Node.js File Upload Libraries
formidableexpress-fileuploadbusboyconnect-busboymulterSimilar Packages:

Node.js File Upload Libraries

These libraries are designed to handle file uploads in Node.js applications. They provide various functionalities to parse incoming multipart/form-data requests, manage file streams, and facilitate the handling of uploaded files. Each library has its own approach and features, making them suitable for different use cases and preferences in web development.

Npm Package Weekly Downloads Trend

3 Years

Github Stars Ranking

Stat Detail

Package
Downloads
Stars
Size
Issues
Publish
License
formidable18,980,265-204 kB-a year agoMIT
express-fileupload489,4421,560119 kB249 months agoMIT
busboy02,998124 kB37--
connect-busboy01564.57 kB0--
multer012,02631.5 kB24725 days agoMIT

Feature Comparison: formidable vs express-fileupload vs busboy vs connect-busboy vs multer

Ease of Use

  • formidable:

    Formidable provides a comprehensive API that simplifies file uploads while offering advanced features, making it relatively easy to use for developers familiar with Node.js.

  • express-fileupload:

    Express-FileUpload is very user-friendly, allowing developers to handle file uploads with minimal setup and clear API methods for accessing files.

  • busboy:

    Busboy is a low-level library that requires more manual handling of streams and file data, which can be challenging for beginners but offers flexibility for advanced users.

  • connect-busboy:

    Connect-Busboy is easy to integrate with existing Connect applications, providing a straightforward way to handle file uploads without much configuration.

  • multer:

    Multer is designed specifically for Express and is straightforward to implement, with clear documentation and examples that make it accessible for developers.

Streaming Support

  • formidable:

    Formidable supports streaming uploads and can handle large files efficiently by processing them in chunks, making it suitable for applications dealing with significant file sizes.

  • express-fileupload:

    Express-FileUpload does not natively support streaming, as it buffers files in memory, which may not be ideal for large uploads.

  • busboy:

    Busboy excels in streaming file uploads, allowing files to be processed as they are being uploaded, which is efficient for large files and reduces memory usage.

  • connect-busboy:

    Connect-Busboy leverages Busboy's streaming capabilities, providing efficient file handling in a middleware format for Connect applications.

  • multer:

    Multer supports streaming uploads but primarily buffers files in memory or on disk, depending on the configuration, which can be a limitation for very large files.

File Size Limitations

  • formidable:

    Formidable allows developers to set file size limits and provides error handling for exceeding those limits, making it robust for file management.

  • express-fileupload:

    Express-FileUpload allows setting file size limits easily through options, providing a straightforward way to control upload sizes.

  • busboy:

    Busboy does not impose file size limits by default, allowing developers to implement their own checks according to application needs.

  • connect-busboy:

    Connect-Busboy inherits Busboy's flexibility regarding file size limits, enabling custom implementations for handling large files.

  • multer:

    Multer allows setting limits on file sizes and number of files uploaded, providing built-in error handling for exceeding these limits.

File Management Features

  • formidable:

    Formidable offers advanced file management features, including file renaming, progress tracking, and automatic parsing, making it suitable for complex applications.

  • express-fileupload:

    Express-FileUpload provides basic file management features, allowing easy access to uploaded files but lacks advanced management options like renaming or organizing files.

  • busboy:

    Busboy focuses on parsing and streaming files but does not provide built-in file management features like renaming or moving files after upload.

  • connect-busboy:

    Connect-Busboy inherits Busboy's minimalistic approach, focusing on parsing rather than file management, requiring additional handling for file storage.

  • multer:

    Multer provides flexible storage options, allowing developers to define custom storage engines and manage file uploads effectively, including renaming and organizing files.

Community and Support

  • formidable:

    Formidable has been around for a while and has a solid community, providing ample resources, documentation, and support for developers.

  • express-fileupload:

    Express-FileUpload has a growing community and good documentation, making it easier to find support and examples for common use cases.

  • busboy:

    Busboy has a smaller community compared to other libraries, which may result in limited support and resources for troubleshooting.

  • connect-busboy:

    Connect-Busboy is less popular but benefits from the Busboy community, offering some support through shared resources and documentation.

  • multer:

    Multer is widely used in the Express community, with extensive documentation and a large user base, ensuring robust support and resources for developers.

How to Choose: formidable vs express-fileupload vs busboy vs connect-busboy vs multer

  • formidable:

    Use Formidable if you need a robust solution that can handle file uploads with additional features like file renaming, progress tracking, and automatic file parsing. It is suitable for applications that require more control and flexibility in managing uploaded files.

  • express-fileupload:

    Select Express-FileUpload if you prefer a simple and easy-to-use middleware for handling file uploads in Express applications. It supports both single and multiple file uploads and provides a straightforward API for accessing uploaded files.

  • busboy:

    Choose Busboy if you need a lightweight and efficient streaming parser for handling file uploads without any additional features. It is ideal for applications that require low-level control over the file upload process and can handle streams directly.

  • connect-busboy:

    Opt for Connect-Busboy if you are using the Connect middleware framework and want a seamless integration for file uploads. It provides a simple way to handle multipart forms in a Connect-based application, leveraging Busboy's capabilities.

  • multer:

    Choose Multer if you are looking for a powerful middleware specifically designed for handling multipart/form-data in Express applications. It offers a variety of storage options, including memory storage and disk storage, and is highly configurable for different use cases.

README for formidable

npm formidable package logo

formidable npm version MIT license Libera Manifesto Twitter

A Node.js module for parsing form data, especially file uploads.

Code style linux build status macos build status

If you have any how-to kind of questions, please read the Contributing Guide and Code of Conduct documents.
For bugs reports and feature requests, please create an issue or ping @wgw_eth / @wgw_lol at Twitter.

Conventional Commits Minimum Required Nodejs Buy me a Kofi Make A Pull Request

This project is semantically versioned and if you want support in migrating between versions you can schedule us for training or support us through donations, so we can prioritize.

[!CAUTION] As of April 2025, old versions like v1 and v2 are still the most used, while they are deperecated for years -- they are also vulnerable to attacks if you are not implementing it properly. Please upgrade! We are here to help, and AI Editors & Agents could help a lot in such codemod-like migrations.

[!TIP] If you are starting a fresh project, you can check out the formidable-mini which is a super minimal version of Formidable (not quite configurable yet, but when it does it could become the basis for formidable@v4), using web standards like FormData API and File API, and you can use it to stream uploads directly to S3 or other such services.

Project Status: Maintained

[!NOTE] Check VERSION NOTES for more information on v1, v2, and v3 plans, NPM dist-tags and branches._

This module was initially developed by @felixge for Transloadit, a service focused on uploading and encoding images and videos. It has been battle-tested against hundreds of GBs of file uploads from a large variety of clients and is considered production-ready and is used in production for years.

Currently, we are few maintainers trying to deal with it. :) More contributors are always welcome! :heart: Jump on issue #412 which is closed, but if you are interested we can discuss it and add you after strict rules, like enabling Two-Factor Auth in your npm and GitHub accounts.

Highlights

Install

This package is a dual ESM/commonjs package.

[!NOTE] This project requires Node.js >= 20. Install it using yarn or npm.
We highly recommend to use Yarn when you think to contribute to this project.

This is a low-level package, and if you're using a high-level framework it may already be included. Check the examples below and the examples/ folder.

# v2
npm install formidable@v2

# v3
npm install formidable
npm install formidable@v3

Note: Future not ready releases will be published on *-next dist-tags for the corresponding version.

Examples

For more examples look at the examples/ directory.

with Node.js http module

Parse an incoming file upload, with the Node.js's built-in http module.

import http from 'node:http';
import formidable, {errors as formidableErrors} from 'formidable';

const server = http.createServer(async (req, res) => {
  if (req.url === '/api/upload' && req.method.toLowerCase() === 'post') {
    // parse a file upload
    const form = formidable({});
    let fields;
    let files;
    try {
        [fields, files] = await form.parse(req);
    } catch (err) {
        // example to check for a very specific error
        if (err.code === formidableErrors.maxFieldsExceeded) {

        }
        console.error(err);
        res.writeHead(err.httpCode || 400, { 'Content-Type': 'text/plain' });
        res.end(String(err));
        return;
    }
    res.writeHead(200, { 'Content-Type': 'application/json' });
    res.end(JSON.stringify({ fields, files }, null, 2));
    return;
  }

  // show a file upload form
  res.writeHead(200, { 'Content-Type': 'text/html' });
  res.end(`
    <h2>With Node.js <code>"http"</code> module</h2>
    <form action="/api/upload" enctype="multipart/form-data" method="post">
      <div>Text field title: <input type="text" name="title" /></div>
      <div>File: <input type="file" name="multipleFiles" multiple="multiple" /></div>
      <input type="submit" value="Upload" />
    </form>
  `);
});

server.listen(8080, () => {
  console.log('Server listening on http://localhost:8080/ ...');
});

with Express.js

There are multiple variants to do this, but Formidable just need Node.js Request stream, so something like the following example should work just fine, without any third-party Express.js middleware.

Or try the examples/with-express.js

import express from 'express';
import formidable from 'formidable';

const app = express();

app.get('/', (req, res) => {
  res.send(`
    <h2>With <code>"express"</code> npm package</h2>
    <form action="/api/upload" enctype="multipart/form-data" method="post">
      <div>Text field title: <input type="text" name="title" /></div>
      <div>File: <input type="file" name="someExpressFiles" multiple="multiple" /></div>
      <input type="submit" value="Upload" />
    </form>
  `);
});

app.post('/api/upload', (req, res, next) => {
  const form = formidable({});

  form.parse(req, (err, fields, files) => {
    if (err) {
      next(err);
      return;
    }
    res.json({ fields, files });
  });
});

app.listen(3000, () => {
  console.log('Server listening on http://localhost:3000 ...');
});

with Koa and Formidable

Of course, with Koa v1, v2 or future v3 the things are very similar. You can use formidable manually as shown below or through the koa-better-body package which is using formidable under the hood and support more features and different request bodies, check its documentation for more info.

Note: this example is assuming Koa v2. Be aware that you should pass ctx.req which is Node.js's Request, and NOT the ctx.request which is Koa's Request object - there is a difference.

import Koa from 'Koa';
import formidable from 'formidable';

const app = new Koa();

app.on('error', (err) => {
  console.error('server error', err);
});

app.use(async (ctx, next) => {
  if (ctx.url === '/api/upload' && ctx.method.toLowerCase() === 'post') {
    const form = formidable({});

    // not very elegant, but that's for now if you don't want to use `koa-better-body`
    // or other middlewares.
    await new Promise((resolve, reject) => {
      form.parse(ctx.req, (err, fields, files) => {
        if (err) {
          reject(err);
          return;
        }

        ctx.set('Content-Type', 'application/json');
        ctx.status = 200;
        ctx.state = { fields, files };
        ctx.body = JSON.stringify(ctx.state, null, 2);
        resolve();
      });
    });
    await next();
    return;
  }

  // show a file upload form
  ctx.set('Content-Type', 'text/html');
  ctx.status = 200;
  ctx.body = `
    <h2>With <code>"koa"</code> npm package</h2>
    <form action="/api/upload" enctype="multipart/form-data" method="post">
    <div>Text field title: <input type="text" name="title" /></div>
    <div>File: <input type="file" name="koaFiles" multiple="multiple" /></div>
    <input type="submit" value="Upload" />
    </form>
  `;
});

app.use((ctx) => {
  console.log('The next middleware is called');
  console.log('Results:', ctx.state);
});

app.listen(3000, () => {
  console.log('Server listening on http://localhost:3000 ...');
});

Benchmarks

The benchmark is quite old, from the old codebase. But maybe quite true though. Previously the numbers was around ~500 mb/sec. Currently with moving to the new Node.js Streams API it's faster. You can clearly see the differences between the Node versions.

Note: a lot better benchmarking could and should be done in future.

Benchmarked on 8GB RAM, Xeon X3440 (2.53 GHz, 4 cores, 8 threads)

~/github/node-formidable master
❯ nve --parallel 8 10 12 13 node benchmark/bench-multipart-parser.js

 ⬢  Node 8

1261.08 mb/sec

 ⬢  Node 10

1113.04 mb/sec

 ⬢  Node 12

2107.00 mb/sec

 ⬢  Node 13

2566.42 mb/sec

benchmark January 29th, 2020

API

Formidable / IncomingForm

All shown are equivalent.

Please pass options to the function/constructor, not by assigning them to the instance form

import formidable from 'formidable';
const form = formidable(options);

Options

See it's defaults in src/Formidable.js DEFAULT_OPTIONS (the DEFAULT_OPTIONS constant).

  • options.encoding {string} - default 'utf-8'; sets encoding for incoming form fields,

  • options.uploadDir {string} - default os.tmpdir(); the directory for placing file uploads in. You can move them later by using fs.rename().

  • options.keepExtensions {boolean} - default false; to include the extensions of the original files or not

  • options.allowEmptyFiles {boolean} - default false; allow upload empty files

  • options.minFileSize {number} - default 1 (1byte); the minium size of uploaded file.

  • options.maxFiles {number} - default Infinity; limit the amount of uploaded files, set Infinity for unlimited

  • options.maxFileSize {number} - default 200 * 1024 * 1024 (200mb); limit the size of each uploaded file.

  • options.maxTotalFileSize {number} - default options.maxFileSize; limit the size of the batch of uploaded files.

  • options.maxFields {number} - default 1000; limit the number of fields, set Infinity for unlimited

  • options.maxFieldsSize {number} - default 20 * 1024 * 1024 (20mb); limit the amount of memory all fields together (except files) can allocate in bytes.

  • options.hashAlgorithm {string | false} - default false; include checksums calculated for incoming files, set this to some hash algorithm, see crypto.createHash for available algorithms

  • options.fileWriteStreamHandler {function} - default null, which by default writes to host machine file system every file parsed; The function should return an instance of a Writable stream that will receive the uploaded file data. With this option, you can have any custom behavior regarding where the uploaded file data will be streamed for. If you are looking to write the file uploaded in other types of cloud storages (AWS S3, Azure blob storage, Google cloud storage) or private file storage, this is the option you're looking for. When this option is defined the default behavior of writing the file in the host machine file system is lost.

  • options.filename {function} - default undefined Use it to control newFilename. Must return a string. Will be joined with options.uploadDir.

  • options.filter {function} - default function that always returns true. Use it to filter files before they are uploaded. Must return a boolean. Will not make the form.parse error

  • options.createDirsFromUploads {boolean} - default false. If true, makes direct folder uploads possible. Use <input type="file" name="folders" webkitdirectory directory multiple> to create a form to upload folders. Has to be used with the options options.uploadDir and options.filename where options.filename has to return a string with the character / for folders to be created. The base will be options.uploadDir.

options.filename {function} function (name, ext, part, form) -> string

where part can be decomposed as

const { originalFilename, mimetype} = part;

Note: If this size of combined fields, or size of some file is exceeded, an 'error' event is fired.

// The amount of bytes received for this form so far.
form.bytesReceived;
// The expected number of bytes in this form.
form.bytesExpected;

options.filter {function} function ({name, originalFilename, mimetype}) -> boolean

Behaves like Array.filter: Returning false will simply ignore the file and go to the next.

const options = {
  filter: function ({name, originalFilename, mimetype}) {
    // keep only images
    return mimetype && mimetype.includes("image");
  }
};

Note: use an outside variable to cancel all uploads upon the first error

Note: use form.emit('error') to make form.parse error

let cancelUploads = false;// create variable at the same scope as form
const options = {
  filter: function ({name, originalFilename, mimetype}) {
    // keep only images
    const valid = mimetype && mimetype.includes("image");
    if (!valid) {
      form.emit('error', new formidableErrors.default('invalid type', 0, 400)); // optional make form.parse error
      cancelUploads = true; //variable to make filter return false after the first problem
    }
    return valid && !cancelUploads;
  }
};

.parse(request, ?callback)

Parses an incoming Node.js request containing form data. If callback is not provided a promise is returned.

const form = formidable({ uploadDir: __dirname });

form.parse(req, (err, fields, files) => {
  console.log('fields:', fields);
  console.log('files:', files);
});

// with Promise
const [fields, files] = await form.parse(req);

You may overwrite this method if you are interested in directly accessing the multipart stream. Doing so will disable any 'field' / 'file' events processing which would occur otherwise, making you fully responsible for handling the processing.

About uploadDir, given the following directory structure

project-name
├── src
│   └── server.js
│
└── uploads
    └── image.jpg

__dirname would be the same directory as the source file itself (src)

 `${__dirname}/../uploads`

to put files in uploads.

Omitting __dirname would make the path relative to the current working directory. This would be the same if server.js is launched from src but not project-name.

null will use default which is os.tmpdir()

Note: If the directory does not exist, the uploaded files are silently discarded. To make sure it exists:

import {createNecessaryDirectoriesSync} from "filesac";


const uploadPath = `${__dirname}/../uploads`;
createNecessaryDirectoriesSync(`${uploadPath}/x`);

In the example below, we listen on couple of events and direct them to the data listener, so you can do whatever you choose there, based on whether its before the file been emitted, the header value, the header name, on field, on file and etc.

Or the other way could be to just override the form.onPart as it's shown a bit later.

form.once('error', console.error);

form.on('fileBegin', (formname, file) => {
  form.emit('data', { name: 'fileBegin', formname, value: file });
});

form.on('file', (formname, file) => {
  form.emit('data', { name: 'file', formname, value: file });
});

form.on('field', (fieldName, fieldValue) => {
  form.emit('data', { name: 'field', key: fieldName, value: fieldValue });
});

form.once('end', () => {
  console.log('Done!');
});

// If you want to customize whatever you want...
form.on('data', ({ name, key, value, buffer, start, end, formname, ...more }) => {
  if (name === 'partBegin') {
  }
  if (name === 'partData') {
  }
  if (name === 'headerField') {
  }
  if (name === 'headerValue') {
  }
  if (name === 'headerEnd') {
  }
  if (name === 'headersEnd') {
  }
  if (name === 'field') {
    console.log('field name:', key);
    console.log('field value:', value);
  }
  if (name === 'file') {
    console.log('file:', formname, value);
  }
  if (name === 'fileBegin') {
    console.log('fileBegin:', formname, value);
  }
});

.use(plugin: Plugin)

A method that allows you to extend the Formidable library. By default we include 4 plugins, which essentially are adapters to plug the different built-in parsers.

The plugins added by this method are always enabled.

See src/plugins/ for more detailed look on default plugins.

The plugin param has such signature:

function(formidable: Formidable, options: Options): void;

The architecture is simple. The plugin is a function that is passed with the Formidable instance (the form across the README examples) and the options.

Note: the plugin function's this context is also the same instance.

const form = formidable({ keepExtensions: true });

form.use((self, options) => {
  // self === this === form
  console.log('woohoo, custom plugin');
  // do your stuff; check `src/plugins` for inspiration
});

form.parse(req, (error, fields, files) => {
  console.log('done!');
});

Important to note, is that inside plugin this.options, self.options and options MAY or MAY NOT be the same. General best practice is to always use the this, so you can later test your plugin independently and more easily.

If you want to disable some parsing capabilities of Formidable, you can disable the plugin which corresponds to the parser. For example, if you want to disable multipart parsing (so the src/parsers/Multipart.js which is used in src/plugins/multipart.js), then you can remove it from the options.enabledPlugins, like so

import formidable, {octetstream, querystring, json} from "formidable";
const form = formidable({
  hashAlgorithm: 'sha1',
  enabledPlugins: [octetstream, querystring, json],
});

Be aware that the order MAY be important too. The names corresponds 1:1 to files in src/plugins/ folder.

Pull requests for new built-in plugins MAY be accepted - for example, more advanced querystring parser. Add your plugin as a new file in src/plugins/ folder (lowercased) and follow how the other plugins are made.

form.onPart

If you want to use Formidable to only handle certain parts for you, you can do something similar. Or see #387 for inspiration, you can for example validate the mime-type.

const form = formidable();

form.onPart = (part) => {
  part.on('data', (buffer) => {
    // do whatever you want here
  });
};

For example, force Formidable to be used only on non-file "parts" (i.e., html fields)

const form = formidable();

form.onPart = function (part) {
  // let formidable handle only non-file parts
  if (part.originalFilename === '' || !part.mimetype) {
    // used internally, please do not override!
    form._handlePart(part);
  }
};

File

export interface File {
  // The size of the uploaded file in bytes.
  // If the file is still being uploaded (see `'fileBegin'` event),
  // this property says how many bytes of the file have been written to disk yet.
  file.size: number;

  // The path this file is being written to. You can modify this in the `'fileBegin'` event in
  // case you are unhappy with the way formidable generates a temporary path for your files.
  file.filepath: string;

  // The name this file had according to the uploading client.
  file.originalFilename: string | null;

  // calculated based on options provided
  file.newFilename: string | null;

  // The mime type of this file, according to the uploading client.
  file.mimetype: string | null;

  // A Date object (or `null`) containing the time this file was last written to.
  // Mostly here for compatibility with the [W3C File API Draft](http://dev.w3.org/2006/webapi/FileAPI/).
  file.mtime: Date | null;

  file.hashAlgorithm: false | |'sha1' | 'md5' | 'sha256'
  // If `options.hashAlgorithm` calculation was set, you can read the hex digest out of this var (at the end it will be a string)
  file.hash: string | object | null;
}

file.toJSON()

This method returns a JSON-representation of the file, allowing you to JSON.stringify() the file which is useful for logging and responding to requests.

Events

'progress'

Emitted after each incoming chunk of data that has been parsed. Can be used to roll your own progress bar. Warning Use this only for server side progress bar. On the client side better use XMLHttpRequest with xhr.upload.onprogress =

form.on('progress', (bytesReceived, bytesExpected) => {});

'field'

Emitted whenever a field / value pair has been received.

form.on('field', (name, value) => {});

'fileBegin'

Emitted whenever a new file is detected in the upload stream. Use this event if you want to stream the file to somewhere else while buffering the upload on the file system.

form.on('fileBegin', (formName, file) => {
    // accessible here
    // formName the name in the form (<input name="thisname" type="file">) or http filename for octetstream
    // file.originalFilename http filename or null if there was a parsing error
    // file.newFilename generated hexoid or what options.filename returned
    // file.filepath default pathname as per options.uploadDir and options.filename
    // file.filepath = CUSTOM_PATH // to change the final path
});

'file'

Emitted whenever a field / file pair has been received. file is an instance of File.

form.on('file', (formname, file) => {
    // same as fileBegin, except
    // it is too late to change file.filepath
    // file.hash is available if options.hash was used
});

'error'

Emitted when there is an error processing the incoming form. A request that experiences an error is automatically paused, you will have to manually call request.resume() if you want the request to continue firing 'data' events.

May have error.httpCode and error.code attached.

form.on('error', (err) => {});

'aborted'

Emitted when the request was aborted by the user. Right now this can be due to a 'timeout' or 'close' event on the socket. After this event is emitted, an error event will follow. In the future there will be a separate 'timeout' event (needs a change in the node core).

form.on('aborted', () => {});

'end'

Emitted when the entire request has been received, and all contained files have finished flushing to disk. This is a great place for you to send your response.

form.on('end', () => {});

Helpers

firstValues

Gets first values of fields, like pre 3.0.0 without multiples pass in a list of optional exceptions where arrays of strings is still wanted (<select multiple> for example)

import { firstValues } from 'formidable/src/helpers/firstValues.js';

// ...
form.parse(request, async (error, fieldsMultiple, files) => {
    if (error) {
        //...
    }
    const exceptions = ['thisshouldbeanarray'];
    const fieldsSingle = firstValues(form, fieldsMultiple, exceptions);
    // ...

readBooleans

Html form input type="checkbox" only send the value "on" if checked, convert it to booleans for each input that is expected to be sent as a checkbox, only use after firstValues or similar was called.

import { firstValues } from 'formidable/src/helpers/firstValues.js';
import { readBooleans } from 'formidable/src/helpers/readBooleans.js';

// ...
form.parse(request, async (error, fieldsMultiple, files) => {
    if (error) {
        //...
    }
    const fieldsSingle = firstValues(form, fieldsMultiple);

    const expectedBooleans = ['checkbox1', 'wantsNewsLetter', 'hasACar'];
    const fieldsWithBooleans = readBooleans(fieldsSingle, expectedBooleans);
    // ...

Changelog

./CHANGELOG.md

Ports & Credits

Contributing

If the documentation is unclear or has a typo, please click on the page's Edit button (pencil icon) and suggest a correction. If you would like to help us fix a bug or add a new feature, please check our Contributing Guide. Pull requests are welcome!

Thanks goes to these wonderful people (emoji key):


Felix Geisendörfer

💻 🎨 🤔 📖

Charlike Mike Reagent

🐛 🚇 🎨 💻 📖 💡 🤔 🚧 ⚠️

Kedar

💻 ⚠️ 💬 🐛

Walle Cyril

💬 🐛 💻 💵 🤔 🚧

Xargs

💬 🐛 💻 🚧

Amit-A

💬 🐛 💻

Charmander

💬 🐛 💻 🤔 🚧

Dylan Piercey

🤔

Adam Dobrawy

🐛 📖

amitrohatgi

🤔

Jesse Feng

🐛

Nathanael Demacon

💬 💻 👀

MunMunMiao

🐛

Gabriel Petrovay

🐛 💻

Philip Woods

💻 🤔

Dmitry Ivonin

📖

Claudio Poli

💻

From a Felix blog post:

  • Sven Lito for fixing bugs and merging patches
  • egirshov for contributing many improvements to the node-formidable multipart parser
  • Andrew Kelley for also helping with fixing bugs and making improvements
  • Mike Frey for contributing JSON support

License

Formidable is licensed under the MIT License.