Giter Club home page Giter Club logo

then-busboy's Introduction

then-busboy

Promise-based wrapper around Busboy. Processes multipart/form-data request body and returns it in a single object.

Code Coverage CI ESLint

Installation

You can install then-busboy from npm:

npm install --save then-busboy

Or with yarn:

yarn add then-busboy

Usage

Because then-busboy can process multipart/form-data bodies from AsyncIterable sources, you can use it with different HTTP frameworks, or with Node.js http module, or even with Response object (see the 4th example).

  1. Let's take a look at the example with http module from Node.js. We'll write a simple server that will parse form-data request, read files content and then send them back as JSON:
import {createServer} from "http"
import {parse} from "then-busboy"

const handler = (req, res) => parse(req)
  .then(async body => {
    const result = []
    for (const [path, value] of body) {
      result.push([path, isFile(value) ? await value.text() : value])
    }

    res.setHeader("Content-Type", "application/json")
    res.end(JSON.stringify(Body.json(result)))
  })
  .catch(error => {
    res.statusCode = error.status || 500
    res.end(error.message)
  })

createServer(handler)
  .listen(2319, () => console.log("Server started on http://localhost:2319"))

Note: You can use asynchronous function syntax, because then-busboy always returns a Promise.

  1. Miniaml multipart middleware implementation for Koa.js:
import {parse} from "then-busboy"

async function multipart(ctx, next) {
  if (["post", "put"].includes(ctx.method.toLowerCase()) === false) {
    return next()
  }

  if (ctx.is("multipart/form-data") === false) {
    return next()
  }

  const body = await parse(ctx.req)

  ctx.request.body = body.json()

  return next()
}

export default multipart
  1. You can also use AsyncIterable value as the input. That way you can parse FormData from other that just HTTP requests. Let's take a loook at the example with form-data-encoder and formdata-node as the input:
import {fileFromPath} from "formdata-node/file-from-path"
import {FormDataEncoder} from "form-data-encoder"
import {FormData} from "formdata-node"
import {parse} from "then-busboy"

const form = new FormData()

form.set("greeting", "Hello, World!")
form.set("file", new File(["On Soviet Moon landscape see binoculars through YOU"], "file.txt"))
form.set("fileFromPath", await fileFromPath("path/to/a/file"))

const encoder = new FormDataEncoder(form)

const body = await parse(encoder, {
  headers: {
    "content-type": encoder.contentType
  }
})

console.log(body.json()) // -> {greeting: string, file: BodyFile, fileFromPath: BodyFile}
  1. Because then-busboy accepts AsyncIterable as the input, you can also read Response body like that:
import {Response, FormData} from "node-fetch"
import {parse} from "then-bosboy"
const form = new FormData()

form.set("greeting", "Hello, World!")
form.set("file", new File(["On Soviet Moon landscape see binoculars through YOU"], "file.txt"))

const response = new Respone(form)
// then-busboy will parse the input and return its own Body instance from which you can create a complete object by calling .json() method or a FormData using .formData() method.
// The difference with Response.formData() is that then-busboy will save all files to file system and create a *reference* to each of them,
// while Response (currently) will dump them into RAM, which can be less efficient in some scenarious
const body = await parse(response.body, {
  headers: {
    "content-type": response.headers.get("content-type")
  }
})

body.json() // -> {greeting: string, file: BodyFile}
body.formData() // -> FormData

API

parse(source[, options]) -> {Promise<Body>}

  • {AsyncIterable} source – Incoming HTTP multipart/form-data source.
  • {object} [options = {}]
    • {boolean} castTypes – allow to restore type of each value (default – true)
    • more information about busboy options here.

class Body

constructor(entries) -> {Body}

Create an object that allows to manipulate FormData fields taken then-busboy

Static methods

from(entries) -> {Body}

Create a new Body from given entries. An alias of new Body(entries)

  • {Array<[string[], any]>} entries – an array of Body initial path-value pairs taken from then-busboy
json(value) -> {object}

Return an object with data taken from given entries or Body

  • {Body | Array<[string[], any]>} – return an object from given Body or entries
formData(value) -> {FormData}

Return a FormData instance with data taken from given entries or Body

  • {Body | Array<[string[], any]>} – return an FormData from given Body or entries

Instance properties

length -> {number}

Return an amount of entries and files in current Body instance

Instance methods

fields() -> {Body}

Return a new Body that contains fields only

files() -> {Body}

Return a new Body that contains files only

json() -> {object}

Return an object with data taken the current Body instance

formData() -> {FormData}

Return a FormData with data taken the current Body instance

entries() -> {Array<[string[], any]>}

Return an array of entries in current Body instance

values() -> {Iterator}

Return an iterator allows to go through the Body values

keys() -> {Iterator}

Return an iterator allows to go through the Body fields path

interface BodyFile

This interface reflects internal representation of a File. It is not meant to be constructed manually, but since it's compatible with files from the browsers, you can use these in Body if you need to.

Instance properties

name

Contains original name of file taken from the filename property within the form-data.

type

File MIME type

enc

Contains a value from transfer encoding header

path

Path to the file on disk

Instance methods

stream() -> {Readable}

Returns a Readable stream allowing to consume file's content

class BodyField

BodyField class in the internal representation of a regular FormData value.

constructor(value: unknown, name: string[, options]) -> {BodyField}

Creates a new instance of the BodyField class.

Instance properties

name

Returns the name of the field.

fieldnameTruncated

Indicates whether the fieldname was truncated.

valueTruncated

Indicates whether the value was truncated.

enc

Returns a value from Content-Transfer-Encoding header.

type

Returns a value from Content-Type header.

Instance methods

valueOf() -> {unknown}

Returns the value of the BodyField.

toString() -> {string}

Returns string representation of the BodyField value.

Fields format

then-busboy can restore an object structure from form-data field names if you will follow the naming formats with dots or square brackets:

Dot notation

This notation looks similarly to JS object properties accessiong syntax:

# Flat objects looks the same in both notations
# Note that the following notation examples is just a pseudo code
name = "John Doe"
age = 25

then-busboy will return the this object for an example from above:

{
  name: "John Doe",

  // By default, non-string values will be converted to their initial type.
  // So, "25" -> 25, "null" -> null, "false" -> false etc.
  age: 25
}

For deep objects or collections, use dot or brackets as a separator. But don't mix them.

  rootField.nestedField = "Some text here"
 {
  rootField: {
    nestedField: "Some text here"
  }
 }

Bracket notation

rootField[nestedField] = "I beat Twilight Sparkle and all I got was this lousy t-shirt"

Becomes

{
  rootField: {
    nestedField: "I beat Twilight Sparkle and all I got was this lousy t-shirt"
  }
}

You can also send an arrays and collections using bracket format:

message[sender] = "John Doe"
message[text] = "Some whatever text message."
message[attachments][0][file] = <here is the file content>
message[attachments][0][description] = "Here is a description of the file"

then-busboy returns the following object:

{
  message: {
    sender: "John Doe",
    text: "Some whatever text message.",
    attachments: [
      {
        "file": File, // this field will be represended as a File instance
        "description": "Here is a description of the file"
      }
    ]
  }
}

Collections allowed too:

[0][firstName] = "John"
[0][lastName] = "Doe"
[0][dob][day] = "1"
[0][dob][month] = "Jan."
[0][dob][year] = "1989"
[0][skills][0] = "Node.js"
[0][skills][1] = "CoffeeScript"
[0][skills][2] = "JavaScript"
[0][skills][3] = "Babel"
[1][firstName] = "Max"
[1][lastName] = "Doe"
[1][dob][day] = "12"
[1][dob][month] = "Mar."
[1][dob][year] = "1992"
[1][skills][0] = "Python"
[1][skills][1] = "Flask"
[1][skills][2] = "JavaScript"
[1][skills][3] = "Babel"
[1][skills][4] = "React"
[1][skills][5] = "Redux"

Then you will receive:

[
  {
    firstName: "John",
    lastName: "Doe",
    dob: {
      day: 1,
      month: "Jan.",
      year: 1989
    },
    skills: ["Node.js", "CoffeeScript", "JavaScript", "Babel"]
  }, {
    firstName: "Max",
    lastName: "Doe",
    dob: {
      day: 12,
      month: "Mar.",
      year: 1992
    },
    skills: ["Python", "Flask", "JavaScript", "Babel", "React", "Redux"]
  }
]

Limits

Whenlimits options are set, then-busboy may reject with HTTP 413 error if specified limit(s) exceeded. That will be a regular error from object http-errors package.

Related links

License

MIT

then-busboy's People

Contributors

baune8d avatar dependabot[bot] avatar diedsmiling avatar eiryyy avatar octet-stream avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

then-busboy's Issues

limiting options not work and not throw an error

hello thanks for your great library
I want to use this library to my fastify server but limits options for busboy not working and not throw an error and file will be uploaded.
this is my code (my handler)

"uploadProfilePicHandler": async( req, reply ) => {
            busboy( req.raw, {
                "limits": {
                    "fieldNameSize": 100,
                    "fieldSize": 1000000,
                    "fields": 1,
                    "fileSize": 1000000,
                    "files": 1,
                    "headerPairs": 2000
                }
            } ).then( ( res ) => {
                console.log( res );
            }, ( err ) => {
                console.log( err );
            } );
}

Empty keys

Empty keys are parsed as empty strings. Is it intended to be so? Maybe they should be parsed into numeric indexes?

postman 2016-10-12 20-57-58

mimeType of Fields

I'd like the ability to check the mimeType of a field. This is possible with a change to onField. To make this generic, I could generate an object with all of the information passed to onField.

In order to support backward compatibility, this would require an option of some sort.

If there is already a way to override onField, that would work with no modifications, but I didn't see any.

File validation

I've thought that a mime-type validation will be a nice feature to have here. For example, we can pass an option with the array of mime-types/mime-type patterns and only files with matched mime-types would be allowed. What do you think about it?

tsc error in parse.ts

> tsc

node_modules/then-busboy/@type/parse.d.ts:4:39 - error TS2503: Cannot find namespace 'busboy'.

4 export interface ParseOptions extends busboy.BusboyConfig {
                                        ~~~~~~


Found 1 error.

I couldn't build it because of this error, so I used -skipLibCheck

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.