Skip to content

Multipart Form Data with Arrays @kubb/plugin-client

Kubb handles multipart/form-data requests with support for arrays, files, and complex data types.

Overview

When your OpenAPI specification defines an endpoint with multipart/form-data content type, Kubb generates code that uses the buildFormData utility to serialize request data into FormData format.

Features

The buildFormData utility:

  • Array Support: Iterates over array elements and appends each value
  • File Handling: Detects and handles Blob and File objects
  • Date Handling: Converts Date objects to ISO strings
  • Type Safety: Handles primitives (strings, numbers, booleans) and complex objects
  • Null/Undefined Filtering: Filters out null and undefined values from arrays

How It Works

Generated Code

For endpoints that accept multipart/form-data, Kubb generates code like this:

src/gen/uploadFile.ts
typescript
import { buildFormData } from './.kubb/config'

export async function uploadFile(data: UploadFileRequest) {
  const formData = buildFormData(data)

  return client({
    method: 'post',
    url: '/upload',
    data: formData,
    headers: { 'Content-Type': 'multipart/form-data' }
  })
}

The buildFormData Utility

The buildFormData function generates in .kubb/config.ts and handles various data types:

.kubb/config.ts
typescript
export function buildFormData<T = unknown>(data: T): FormData {
  const formData = new FormData()

  function appendData(key: string, value: any) {
    if (value instanceof Blob) {
      formData.append(key, value)
      return
    }
    if (value instanceof Date) {
      formData.append(key, value.toISOString())
      return
    }
    if (typeof value === 'number' || typeof value === 'boolean') {
      formData.append(key, String(value))
      return
    }
    if (typeof value === 'string') {
      formData.append(key, value)
      return
    }
    if (typeof value === 'object') {
      // Wrap JSON data in a Blob with application/json content type to ensure
      // servers correctly interpret the data. Without this, many servers return
      // 415 Unsupported Media Type errors when receiving JSON in multipart requests.
      formData.append(key, new Blob([JSON.stringify(value)], { type: 'application/json' }))
      return
    }
  }

  if (data) {
    Object.entries(data).forEach(([key, value]) => {
      if (value === undefined || value === null) return

      if (Array.isArray(value)) {
        for (const valueItem of value) {
          if (valueItem === undefined || valueItem === null) continue
          appendData(key, valueItem)
        }
      } else {
        appendData(key, value)
      }
    })
  }

  return formData
}

Usage Examples

Uploading Files with Metadata

src/main.ts
typescript
import { uploadFile } from './gen/uploadFile'

// Upload a single file with metadata
const file = new File(['content'], 'document.pdf', { type: 'application/pdf' })

await uploadFile({
  file: file,
  title: 'My Document',
  tags: ['important', 'work', 'pdf'],
  uploadDate: new Date()
})

Uploading Multiple Files

src/main.ts
typescript
import { uploadFiles } from './gen/uploadFiles'

const files = [
  new File(['content1'], 'doc1.pdf', { type: 'application/pdf' }),
  new File(['content2'], 'doc2.pdf', { type: 'application/pdf' })
]

await uploadFiles({
  files: files,
  category: 'documents'
})

Complex Data with Arrays

src/main.ts
typescript
import { createPost } from './gen/createPost'

await createPost({
  title: 'My Post',
  tags: ['javascript', 'typescript', 'kubb'],
  images: [
    new File(['img1'], 'image1.jpg', { type: 'image/jpeg' }),
    new File(['img2'], 'image2.jpg', { type: 'image/jpeg' })
  ],
  metadata: {
    author: 'John Doe',
    publishDate: new Date()
  }
})

Data Type Handling

Primitives

  • Strings: Append as-is
  • Numbers: Convert to string
  • Booleans: Convert to string ("true" or "false")

Special Types

  • Blob/File: Append directly to FormData
  • Date: Convert to ISO string format
  • Objects: Wrap in a Blob with Content-Type: application/json to enable proper server-side parsing. This prevents 415 (Unsupported Media Type) errors that occur when sending JSON data without the correct content type in multipart requests.

Arrays

  • Each array element appends individually with the same key
  • null and undefined elements filter automatically
  • Nested arrays and objects within arrays process recursively

Null/Undefined Values

  • Top-level null or undefined values skip
  • Array elements that are null or undefined filter out
  • This prevents "null" or "undefined" string literals in FormData

OpenAPI Schema Example

Here's an example OpenAPI specification that generates multipart/form-data handling:

openapi.yaml
yaml
paths:
  /upload:
    post:
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              properties:
                files:
                  type: array
                  items:
                    type: string
                    format: binary
                title:
                  type: string
                tags:
                  type: array
                  items:
                    type: string
                metadata:
                  type: object
                  properties:
                    author:
                      type: string
                    date:
                      type: string
                      format: date-time

Best Practices

  1. File Validation: Validate file types and sizes before calling the generated functions
  2. Error Handling: Wrap calls in try-catch blocks to handle upload errors
  3. Progress Tracking: Use the underlying client's progress events for large uploads
  4. Array Limits: Be mindful of server-side limits on array sizes

Troubleshooting

Arrays Not Working

If arrays do not send correctly, use Kubb version 4.5.14 or later, which includes array support fixes.

Files Not Uploading

Pass actual File or Blob objects, not file paths or base64 strings.

Date Format Issues

Dates convert automatically to ISO strings. If your server expects a different format, pre-process the date before passing it to the generated function.

JSON Object Content-Type

When sending JSON objects in multipart form data, Kubb wraps them in a Blob with Content-Type: application/json. This ensures servers can parse the JSON data correctly. Without the proper content type, many servers return a 415 (Unsupported Media Type) error.

To customize object serialization, pre-process objects before passing to the generated function, or provide your own buildFormData implementation.

Released under the MIT License.