Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Multipart and Binary Bodies

OpenAPI uses the same type: string, format: binary schema shape in several places, but generated clients intentionally expose different APIs depending on the HTTP body.

  • Multipart binary parts use a generated upload wrapper so callers can provide a filename.
  • Raw application/octet-stream request bodies stay as raw bytes or blob values.
  • Binary response bodies stay as raw bytes or blob values.
  • JSON and text multipart parts keep their normal generated model or scalar types.

See examples/multipart-binary/openapi.yaml for a complete spec.

Multipart Uploads

For object-shaped multipart/form-data request bodies, openapi-nexus generates an operation-specific request body model. Binary properties in that model use an upload wrapper instead of the normal raw binary type.

Given this request body:

requestBody:
  required: true
  content:
    multipart/form-data:
      schema:
        type: object
        required: [file, profile, purpose]
        properties:
          file:
            type: string
            format: binary
          profile:
            $ref: '#/components/schemas/ProfileAttributes'
          purpose:
            type: string
      encoding:
        file:
          contentType: image/png
        profile:
          contentType: application/json
        purpose:
          contentType: text/plain

The generated request model has a binary file upload field, a normal JSON profile field, and a normal text purpose field. The multipart field name remains file; the filename is read from the upload wrapper. If the caller does not provide a filename, generated clients fall back to the field name.

Upload Filenames

Use the wrapper when the filename matters, for example when the multipart field name is file but the uploaded filename should be avatar.png.

TypeScript

TypeScript accepts browser-native File values directly. It also accepts { data: Blob, filename?: string } for runtimes or tests that have Blob but not File.

import { AvatarsApi } from './generated/apis/AvatarsApi';

const api = new AvatarsApi();

await api.uploadAvatar({
  body: {
    file: { data: new Blob([bytes], { type: 'image/png' }), filename: 'avatar.png' },
    profile: { display_name: 'Ada Lovelace' },
    purpose: 'profile',
  },
});

await api.uploadAvatar({
  body: {
    file: new File([bytes], 'avatar.png', { type: 'image/png' }),
    profile: { display_name: 'Ada Lovelace' },
    purpose: 'profile',
  },
});

Passing a plain Blob is still accepted, but the filename falls back to the multipart field name.

Go

body := &models.UploadAvatarMultipartRequestBody{
	File: runtime.NewUploadFile(pngBytes, "avatar.png"),
	Profile: models.ProfileAttributes{
		DisplayName: "Ada Lovelace",
	},
	Purpose: "profile",
}

resp, err := avatars.UploadAvatar(ctx, body)

Use runtime.NewUploadFileBytes(pngBytes) when the fallback filename is acceptable.

Python

The same UploadFile wrapper is generated for python-httpx and python-requests.

from generated.models.upload_avatar_multipart_request_body import UploadAvatarMultipartRequestBody
from generated.models.profile_attributes import ProfileAttributes
from generated.runtime import UploadFile

body = UploadAvatarMultipartRequestBody(
    file=UploadFile.from_bytes(png_bytes, filename="avatar.png"),
    profile=ProfileAttributes(display_name="Ada Lovelace"),
    purpose="profile",
)

client.avatars.upload_avatar(body=body)

Use UploadFile.from_bytes(png_bytes) when the fallback filename is acceptable.

Java

UploadAvatarMultipartRequestBody body = new UploadAvatarMultipartRequestBody(
    UploadFile.of(pngBytes, "avatar.png"),
    new ProfileAttributes(null, "Ada Lovelace"),
    "profile"
);

UploadAvatarResponse response = avatarsApi.uploadAvatar(body);

Use UploadFile.ofBytes(pngBytes) when the fallback filename is acceptable.

Kotlin

val body = UploadAvatarMultipartRequestBody(
    file = UploadFile(pngBytes, "avatar.png"),
    profile = ProfileAttributes(displayName = "Ada Lovelace"),
    purpose = "profile",
)

val response = avatarsApi.uploadAvatar(body)

Use UploadFile(pngBytes) when the fallback filename is acceptable.

Rust

The same UploadFile wrapper is generated for rust-reqwest, rust-ureq, and rust-aioduct.

#![allow(unused)]
fn main() {
let body = UploadAvatarMultipartRequestBody {
    file: UploadFile::new(png_bytes, "avatar.png"),
    profile: ProfileAttributes {
        display_name: "Ada Lovelace".to_string(),
        alt_text: None,
    },
    purpose: "profile".to_string(),
};

let response = avatars_api.upload_avatar(&body).await?;
}

Use UploadFile::from_bytes(png_bytes) when the fallback filename is acceptable.

Raw Binary Bodies

For non-multipart application/octet-stream bodies, openapi-nexus does not use the upload wrapper. The method body type remains the normal binary type for the target language:

GeneratorRequest body type
typescript-fetchBlob | File
go-http[]byte
python-httpxbytes
python-requestsbytes
java-okhttpbyte[]
kotlin-okhttpByteArray
rust-reqwestVec<u8>
rust-ureqVec<u8>
rust-aioductVec<u8>

This keeps ordinary binary request bodies and binary responses separate from multipart filename handling.

Supported Multipart Shape

Multipart request bodies must be object-shaped. Each object property becomes one multipart part.

  • Binary parts use UploadFile or UploadFileInput.
  • String, number, boolean, and enum parts are emitted as text.
  • Object and array parts are emitted as JSON.
  • encoding.<part>.contentType controls the per-part Content-Type when present.

Schemas that do not describe an object-shaped multipart body are rejected by generators with an explicit unsupported multipart error.