Skip to content

02Guides

Request bodies and uploads

Several media types, form data, multipart uploads, binary and XML bodies.

Every media type a specification declares for a request body is typed, including form data, multipart uploads, binary bodies and formats swaggerz does not serialize itself.

The body is the body member of params, next to the parameters. JSON bodies are serialized with JSON.stringify after dates are encoded:

import { createAccount } from './generated/api';
await createAccount({ body: { name: 'Ada' } });

When an operation accepts several media types, params becomes a union discriminated by contentType. Leaving it out selects the default (the first available of JSON, multipart, form-urlencoded, text and binary); each other media type has a body type of its own, listed in <Name>Bodies:

import { createAccount } from './generated/api';
await createAccount({ body: { name: 'Ada' } }); // application/json
await createAccount({ contentType: 'application/x-www-form-urlencoded', body: { name: 'Ada' } });
await createAccount({
contentType: 'application/xml',
body: '<account><name>Ada</name></account>',
});

XML and other text media types that the runtime does not serialize take the body already serialized: a string, Blob, ArrayBuffer or ArrayBufferView.

application/x-www-form-urlencoded bodies send arrays as repeated fields and nested objects as JSON, unless the property’s encoding says otherwise: style: deepObject sends metadata[order_id]=6735, an exploded form style sends each property as its own field, and other styles join values with their delimiter.

multipart/form-data bodies send Blobs, Files and bytes as file parts, and arrays as repeated parts:

import { uploadAvatar } from './generated/api';
declare const input: HTMLInputElement; // an <input type="file">
const file = input.files?.[0];
if (file !== undefined)
await uploadAvatar({ accountId: 'a-1', body: { image: file, caption: 'Me' } });

A File keeps its name; other binary values are sent as blob. Object properties become JSON parts labeled application/json (OpenAPI’s default), and a property whose encoding names one contentType is labeled with it.

Bodies made only of fields and files are sent as FormData, so the transport sets the multipart boundary. FormData can only label a part with a Content-Type by turning it into a file upload, so when a part needs one (a JSON object, or an encoding.contentType), the runtime writes the multipart body itself, with its own boundary. A FormData you build yourself is sent unchanged.

format: binary, Swagger 2.0 type: file and binary media types (application/octet-stream, image/*, …) are Blobs. Request bodies also accept ArrayBuffer and typed arrays. Binary responses resolve with a Blob; pass parseAs: 'stream' to read the body as it arrives instead.

  • XML is neither serialized nor parsed: requests take a serialized string, responses return text.
  • A wildcard request media type (*/*, text/*) is never sent as Content-Type: JSON bodies go out as application/json, other bodies with the transport’s default.
  • OpenAPI 3.2 positional multipart encoding (itemEncoding, prefixEncoding) is ignored.

What's generated

bodies.yaml
openapi: 3.1.0
info:
title: Accounts
version: '1.0'
paths:
/accounts:
post:
tags: [accounts]
operationId: createAccount
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/NewAccount'
application/x-www-form-urlencoded:
schema:
$ref: '#/components/schemas/NewAccount'
application/xml:
schema:
$ref: '#/components/schemas/NewAccount'
responses:
'204':
description: Created.
/accounts/{accountId}/avatar:
put:
tags: [accounts]
operationId: uploadAvatar
parameters:
- name: accountId
in: path
required: true
schema:
type: string
requestBody:
required: true
content:
multipart/form-data:
schema:
type: object
required: [image]
properties:
image:
type: string
format: binary
caption:
type: string
responses:
'204':
description: Uploaded.
components:
schemas:
NewAccount:
type: object
required: [name]
properties:
name:
type: string

accounts/create-account.ts

/* eslint-disable */
// @generated by swaggerz. Do not edit: this file is overwritten on the next generation.
import { type RequestOptions, client } from '../client.js';
import type { NewAccount } from '../models/new-account.js';
import { type ApiError, type RequestError, isApiError } from '../runtime/errors.js';
import type { HttpStatus } from '../runtime/types.js';
/** Request body of {@link createAccount} (`application/json`). */
export type CreateAccountBody = NewAccount;
/** Request bodies of {@link createAccount} by media type. Select one with `contentType`. */
export type CreateAccountBodies = {
'application/json': NewAccount;
'application/x-www-form-urlencoded': NewAccount;
'application/xml': string | Blob | ArrayBuffer | ArrayBufferView;
};
/** Parameters of {@link createAccount}. */
export type CreateAccountParams =
| {
/** Media type of the request body. Default `application/json`. */
contentType?: 'application/json' | 'application/x-www-form-urlencoded' | undefined;
/** Request body. */
body: CreateAccountBody;
}
| {
/** Media type of the request body. */
contentType: 'application/xml';
/** Request body. */
body: CreateAccountBodies['application/xml'];
};
/** Response data of {@link createAccount}. */
export type CreateAccountResponse = void;
/** Body of an error response of {@link createAccount}. */
export type CreateAccountErrorBody = unknown;
/**
* The `ApiError`s {@link createAccount} rejects with: one per documented status, so checking `status` narrows `body`.
*
* The last member covers statuses the specification does not document.
*/
export type CreateAccountApiError = ApiError<unknown, HttpStatus<1 | 3 | 4 | 5>>;
/** Everything {@link createAccount} rejects with: an `ApiError` for an error status, or a `RequestError` when no usable response arrived. */
export type CreateAccountError = CreateAccountApiError | RequestError;
/**
* Calls `POST /accounts`.
*
* @remarks `POST /accounts` (operationId `createAccount`)
* @param params - Request body.
* @param options - Per-request options: `signal`, `timeout`, `headers`, `client`.
* @returns `204` Created
* @throws {@link ApiError} When the server responds with a non-2xx status; narrow it with {@link isCreateAccountApiError}.
* @throws {@link RequestError} When no usable response is received (network failure, timeout or invalid body).
*/
export function createAccount(params: CreateAccountParams, options?: RequestOptions): Promise<CreateAccountResponse> {
return (options?.client ?? client).request<CreateAccountResponse>(
{
operationId: 'createAccount',
method: 'POST',
path: '/accounts',
body: params.body,
mediaType: (params.contentType ?? 'application/json'),
},
options,
);
}
/** Whether `error` is an `ApiError` rejected by {@link createAccount}. It narrows to {@link CreateAccountApiError}, so checking `status` afterwards narrows `body` too. */
export function isCreateAccountApiError(error: unknown): error is CreateAccountApiError {
return (
isApiError(error) &&
error.operationId === 'createAccount' &&
error.method === 'POST'
);
}

accounts/upload-avatar.ts

/* eslint-disable */
// @generated by swaggerz. Do not edit: this file is overwritten on the next generation.
import { type RequestOptions, client } from '../client.js';
import { type ApiError, type RequestError, isApiError } from '../runtime/errors.js';
import type { HttpStatus } from '../runtime/types.js';
/** Request body of {@link uploadAvatar} (`multipart/form-data`). */
export type UploadAvatarBody = {
image: Blob | ArrayBuffer | ArrayBufferView;
caption?: string;
};
/** Parameters of {@link uploadAvatar}. */
export type UploadAvatarParams = {
accountId: string;
/** Request body. */
body: UploadAvatarBody;
};
/** Response data of {@link uploadAvatar}. */
export type UploadAvatarResponse = void;
/** Body of an error response of {@link uploadAvatar}. */
export type UploadAvatarErrorBody = unknown;
/**
* The `ApiError`s {@link uploadAvatar} rejects with: one per documented status, so checking `status` narrows `body`.
*
* The last member covers statuses the specification does not document.
*/
export type UploadAvatarApiError = ApiError<unknown, HttpStatus<1 | 3 | 4 | 5>>;
/** Everything {@link uploadAvatar} rejects with: an `ApiError` for an error status, or a `RequestError` when no usable response arrived. */
export type UploadAvatarError = UploadAvatarApiError | RequestError;
/**
* Calls `PUT /accounts/{accountId}/avatar`.
*
* @remarks `PUT /accounts/{accountId}/avatar` (operationId `uploadAvatar`)
* @param params - Request parameters and body.
* @param options - Per-request options: `signal`, `timeout`, `headers`, `client`.
* @returns `204` Uploaded
* @throws {@link ApiError} When the server responds with a non-2xx status; narrow it with {@link isUploadAvatarApiError}.
* @throws {@link RequestError} When no usable response is received (network failure, timeout or invalid body).
*/
export function uploadAvatar(params: UploadAvatarParams, options?: RequestOptions): Promise<UploadAvatarResponse> {
return (options?.client ?? client).request<UploadAvatarResponse>(
{
operationId: 'uploadAvatar',
method: 'PUT',
path: '/accounts/{accountId}/avatar',
pathParams: { accountId: params.accountId },
body: params.body,
mediaType: 'multipart/form-data',
},
options,
);
}
/** Whether `error` is an `ApiError` rejected by {@link uploadAvatar}. It narrows to {@link UploadAvatarApiError}, so checking `status` afterwards narrows `body` too. */
export function isUploadAvatarApiError(error: unknown): error is UploadAvatarApiError {
return (
isApiError(error) &&
error.operationId === 'uploadAvatar' &&
error.method === 'PUT'
);
}
Generated by swaggerz 0.1.2, at build time.