Skip to content

Commit

Permalink
Improve documentation. Small improvements for objectToFormData
Browse files Browse the repository at this point in the history
  • Loading branch information
octet-stream committed Feb 16, 2024
1 parent 29b4ed9 commit 8e0e06f
Show file tree
Hide file tree
Showing 2 changed files with 145 additions and 16 deletions.
147 changes: 134 additions & 13 deletions readme.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
# object-to-form-data

Transform an object/collection to FormData.
Good to use with [then-busboy](https://github.com/octet-stream/then-busboy)
Serialize objects, arrays and collections into [`FormData`](https://developer.mozilla.org/en-US/docs/Web/API/FormData).

[![Code Coverage](https://codecov.io/github/octet-stream/object-to-form-data/coverage.svg?branch=master)](https://codecov.io/github/octet-stream/object-to-form-data?branch=master)
[![CI](https://github.com/octet-stream/object-to-form-data/workflows/CI/badge.svg)](https://github.com/octet-stream/object-to-form-data/actions/workflows/ci.yml)
Expand All @@ -22,24 +21,28 @@ npm i @octetstream/object-to-form-data

## Usage

```js
1. To use this package, just import `objectToFormData` and pass `input` data as the first argument:

```ts
import {objectToFormData} from "@octetstream/object-to-form-data"

const object = {
message: {
sender: "Glim Glam",
text: "Can you believe it, Trixie?",
attachments: [
const user = {
name: "The Octocat",
login: "octocat",
url: "https://github.com/octocat",
repositories: {
nodes: [
{
file: File, // this field will be represented as a window.File instance
description: "I beat Twilight Sparkle and all I got was this lousy t-shirt."
name: "Hello-World",
description: "My first repository on GitHub!",
url: "https://github.com/octocat/Hello-World"
}
]
}
}

// You will receive a FormData instance with all fields of given object
const form = objectToFormData(object)
const form = objectToFormData(user)

const options = {
method: "post",
Expand All @@ -49,11 +52,112 @@ const options = {
const response = await fetch("https://httpbin.org/post", options)
```

The `user` object from this example will be serailized into FormData with following structure (pseudo code):

```
name = "The Octocat"
login = "octocat"
url = "https://github.com/octocat"
repositories[nodes][0][name] = Hello-World
repositories[nodes][0][description] = My first repository on GitHub!
repositories[nodes][0][url] = "https://github.com/octocat/Hello-World"
```

2. By default the `bracked` notation is applied, but use can pass `dot` as `notation` option like this:

```ts
import {objectToFormData} from "@octetstream/object-to-form-data"

const person = {
name: "Nick K.",
url: "https://github.com/octet-stream",
skills: ["TypeScript", "JavaScript", "React", "Next.js", "Vue", "Nuxt", "Qwik", "Docker"]
}

const form = objectToFormData(person, {
notation: "dot" // This will enable dot notation for serialization
})
```

Returned `form` object has following structure:

```
name = "Nick K."
url = "https://github.com/octet-stream"
skills.0 = TypeScript
skills.1 = JavaScript
skills.2 = React
skills.3 = Next.js
skills.4 = Vue
skills.5 = Nuxt
skills.6 = Qwik
skills.7 = Docker
```

3. You can also pass collections as the input:

```ts
import {objectToFormData} from "@octetstream/object-to-form-data"

const developers = [
{
name: "John Doe",
skills: ["JavaScript", "TypeScript", "React", "Qwik"],
isHireable: true
},
{
name: "Max Doe",
skills: ["Python", "Django", "Flask", "MySQL", "Pony ORM"],
isHireable: false
}
]

const form = objectToFormData(person)
```

This results in following `form` structure:

```
[0][name] = "John Doe"
[0][skills][0] = "JavaScript"
[0][skills][1] = "TypeScript"
[0][skills][2] = "React"
[0][skills][3] = "Qwik"
[1][name] = "Max Doe"
[1][skills][0] = "Python"
[1][skills][1] = "Django"
[1][skills][2] = "Flask"
[1][skills][3] = "MySQL"
[1][skills][4] = "Pony ORM"
```

4. Flat arrays supported too:

```ts
import {objectToFormData} from "@octetstream/object-to-form-data"

const fruits = ["orange", "pineapple", "nectarine", "pear", "pomegranate"]

const form = objectToFormData(fruits)
```

Result:

```
[0] = "orange"
[1] = "pineapple"
[2] = "nectarine"
[3] = "pear"
[4] = "pomegranate"
```

## API

### `objectToFormData(input[, options]): FormData`

Indicates whether or not to omit every `false` values. Applied enabled. Does not affect boolean array values
Serializes objects, arrays and collections into `FormData`.

Nested objects will be flattened using either dot or bracket notation.

This function takes following arguments:

Expand All @@ -62,6 +166,8 @@ This function takes following arguments:
| input | `unknown[] \| Record<sting \| number, unknown>` | true || An object to transform |
| options | [`ObjectToFormDataOptions`](#interface-objecttoformdataoptions) | false | `undefined` | Additional serialization options |

Returns `FormData` instance.

### `interface ObjectToFormDataOptions`

Serialization options
Expand All @@ -77,7 +183,7 @@ Serialization options

Value normalizer.

This function will be called on each *scalar* value, before it's added to FormData instance. It **must** return either `Blob` or `string`
Will be called on each *scalar* value, before it's added to FormData instance. It **must** return either `Blob` or `string`

This function will be called with the following arguments:

Expand All @@ -86,3 +192,18 @@ This function will be called with the following arguments:
| value | `unknown` | Current entry value |
| name | `string` | The name of the entry |
| path | `Array<string \| number>` | Entry's path within original object |

This function **must** return either `Blob` or `string`. Any unsupported type will be converted to string by `FormData`.

## Related links

- [`FormData`](https://developer.mozilla.org/en-US/docs/Web/API/FormData) documentation on MDN
- [`File`](https://developer.mozilla.org/en-US/docs/Web/API/File) documentation on MDN
- [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob) documentation on MDN
- [`FormDataValue`](https://developer.mozilla.org/en-US/docs/Web/API/FormDataEntryValue) documentation on MDN.
- [`formdata-node`](https://github.com/octet-stream/form-data) Spec-compliant `FormData` implementation for Node.js.
- [`formdata-polyfill`](https://github.com/jimmywarting/FormData) HTML5 `FormData` for Browsers & NodeJS.
- [`node-fetch`](https://github.com/node-fetch/node-fetch) a light-weight module that brings the Fetch API to Node.js
- [`fetch-blob`](https://github.com/node-fetch/fetch-blob) a Blob implementation for Node.js, originally from `node-fetch`.
- [`form-data-encoder`](https://github.com/octet-stream/form-data-encoder) spec-compliant `multipart/form-data` encoder implementation.
- [`then-busboy`](https://github.com/octet-stream/then-busboy) a promise-based wrapper around Busboy. Process `multipart/form-data` content and returns it as a single object. Will be helpful to handle your data on the server-side applications.
14 changes: 11 additions & 3 deletions src/objectToFormData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ import {isFunction} from "./utils/isFunction.js"
import {isBoolean} from "./utils/isBoolean.js"
import type {Input} from "./Input.js"

/**
* @internal
*/
type Methods = "set" | "append"

/**
* Value normalizer.
*
Expand All @@ -22,6 +27,9 @@ export type NormalizeValue = (

const noopNormalizeValue: NormalizeValue = value => value as string | Blob // Cast value type because FormData will normalize it anyway

/**
* Serialization options
*/
export interface ObjectToFormDataOptions {
/**
* Indicates whether or not to omit every `false` values. Applied enabled. Does not affect boolean array values
Expand Down Expand Up @@ -147,15 +155,15 @@ export const objectToFormData: ObjectToFormData = (
? {strict: optionsOrStrict}
: {...optionsOrStrict}

const form = new CustomFormData()

// Choose the serialization method for browsers which
// are support FormData API partially
// See: https://caniuse.com/#search=formdata
const method: "set" | "append" = isFunction(CustomFormData.prototype.set)
const method: Methods = isFunction(CustomFormData.prototype.set)
? "set"
: "append"

const form = new CustomFormData()

for (const [path, raw] of createIterator(input, strict)) {
const name = pathToString(path, notation)
const value = normalizeValue(raw, name, path)
Expand Down

0 comments on commit 8e0e06f

Please sign in to comment.