Skip to content

Multipart requests

Multipart requests combine one or more sets of data into a single body, separated by boundaries. This is commonly used to upload files.

To define a multipart request in HTTP you must set the contentType header to "multipart/form-data" or multipart/mixed as described in the content types doc and add a multipart body property decorated with @multipartBody

Try it
main.tsp
import "@typespec/http";
using Http;
op create(
@header contentType: "multipart/form-data",
@multipartBody body: {
username: HttpPart<string>;
avatar: HttpPart<File>;
},
): void;

Each property of the body represents a part of the multipart request. The name of the property is used as the name of the part. Properties in models are ordered and the order is used to determine the order of the parts in the request. Each property part must be of type HttpPart<T>

The previous example would correspond to the following HTTP request:

POST / HTTP/1.1
Content-Length: 428
Content-Type: multipart/form-data; boundary=abcde12345
--abcde12345
Content-Disposition: form-data; name="username"
Content-Type: text/plain
typespector
--abcde12345
Content-Disposition: form-data; name="avatar"; filename="image1.png"
Content-Type: application/octet-stream
{…file content…}
--abcde12345--

Default handling

By default, the Content-Type of individual request parts is set automatically according to the type of the schema properties that describe the request parts:

Schema Property TypeContent-TypeExample
Primitivetext/plainHttpPart<string>
HttpPart<number>
HttpPart<boolean>
Complex value or array of complex valuesapplication/jsonHttpPart<Address>
HttpPart<Address[]>
File, bytesapplication/octet-streamHttpPart<File>
HttpPart<bytes>

See Content types for more information about how the HTTP library handles the Content-Type header.

Part names

There is multiple ways to define a part name. The priority is as follows:

  1. Explicit name provied in the HttpPart options (e.g. HttpPart<File, #{ name: "avatar" }>)
  2. Model property name (e.g. name1: HttpPart<string>;)
// Part name here is name2
op create(
@header contentType: "multipart/form-data",
@multipartBody body: {
name1: HttpPart<string, #{ name: "name2" }>;
},
): void;

Unamed parts

When using multipart/mixed parts are not required to be named. Instead of passing a model as the multipart body you can pass a tuple with each part

model Address {
street: string;
city: string;
}
op create(
@header contentType: "multipart/mixed",
@multipartBody body: [
HttpPart<string>,
HttpPart<File, #{ name: "avatar" }>, // An name can also be provided this way
HttpPart<Address>,
HttpPart<File>[]
],
): void;

HttpPart<Foo>[] vs HttpPart<Foo[]>

  • HttpPart<Foo>[] Represent multiple parts of type Foo
  • HttpPart<Foo[]> Represent a single part of type Foo[]

Examples

// Upload a single file
op create(
@header contentType: "multipart/form-data",
@multipartBody body: {
avatar: HttpPart<File>;
},
): void;
// Upload multiple files
op create(
@header contentType: "multipart/form-data",
@multipartBody body: {
avatar: HttpPart<File>;
banner: HttpPart<File>;
},
): void;
// Upload many files
op create(
@header contentType: "multipart/form-data",
@multipartBody body: {
images: HttpPart<File>[];
},
): void;
// Upload 2 form fields
op create(
@header contentType: "multipart/form-data",
@multipartBody body: {
firstName: HttpPart<string>;
lastName: HttpPart<string>;
},
): void;
// Send a json field
model Address {
street: string;
city: string;
}
op create(
@header contentType: "multipart/form-data",
@multipartBody body: {
address: HttpPart<Address>;
},
): void;
// Send multiple fields - In this scenario each address is sent as an individual part
model Address {
street: string;
city: string;
}
op create(
@header contentType: "multipart/form-data",
@multipartBody body: {
addresses: HttpPart<Address>[];
},
): void;

Custom content type

The first template parameter given to HttpPart follow the same logic as a property annotated with @bodyRoot. This means you can include headers, implicit body properties or an explicit body inside.

Try it
main.tsp
import "@typespec/http";
using Http;
model Png extends File {
@header contentType: "image/png";
}
op create(
@header contentType: "multipart/form-data",
@multipartBody body: {
avatar: HttpPart<Png>;
data: HttpPart<{
name: string;
@header contentType: "application/xml";
}>;
},
): void;