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
BlobandFileobjects - Date Handling: Converts
Dateobjects to ISO strings - Type Safety: Handles primitives (strings, numbers, booleans) and complex objects
- Null/Undefined Filtering: Filters out
nullandundefinedvalues from arrays
How It Works
Generated Code
For endpoints that accept multipart/form-data, Kubb generates code like this:
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:
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
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
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
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/jsonto 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
nullandundefinedelements filter automatically- Nested arrays and objects within arrays process recursively
Null/Undefined Values
- Top-level
nullorundefinedvalues skip - Array elements that are
nullorundefinedfilter 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:
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-timeBest Practices
- File Validation: Validate file types and sizes before calling the generated functions
- Error Handling: Wrap calls in try-catch blocks to handle upload errors
- Progress Tracking: Use the underlying client's progress events for large uploads
- 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.
