Skip to main content

Using bodies

Bodies are used to send data in a request and receive data in a response. Request bodies are commonly used to include a payload of data that the server will process, whereas response bodies may contain the result returned by the server. A body can contain various types of data, such as JSON, XML, and binary data such as images and files.

Default body parsing​

@zimic/interceptor automatically parses request and response bodies based on their content-type header:

content-typeParsed as
application/jsonJSON
application/xmlString
application/x-www-form-urlencodedHttpSearchParams
application/* (others)Blob
multipart/form-dataHttpFormData
multipart/* (others)Blob
text/*String
image/*Blob
audio/*Blob
font/*Blob
video/*Blob
*/* (others)JSON if possible, otherwise Blob

Using request bodies​

Request bodies can be declared in your schema using the request.body property. Then, they will be available in the body property, such as in handler.with(), computed responses, and handler.requests.

JSON request body​

JSON bodies are one of the most common ways to send data in requests. To use a JSON body in a request, declare its type in your schema.

schema.ts
import { HttpSchema } from '@zimic/http';

interface User {
id: string;
username: string;
}

type Schema = HttpSchema<{
'/users': {
POST: {
request: {
body: { username: string };
};
response: {
201: { body: User };
};
};
};
}>;
interceptor
.post('/users')
.with({
body: { username: 'me' },
})
.respond({
status: 201,
body: user,
})
.times(1);
TIP: content-type header inference

@zimic/http automatically infers the type of the content-type header as application/json if the request body is a JSON type. You can override this behavior by explicitly setting a different type in your schema.

schema.ts
type Schema = HttpSchema<{
'/users': {
POST: {
request: {
headers: {
'content-type': 'application/json; charset=utf-8';
};
body: { username: string };
};
response: {
201: { body: User };
};
};
};
}>;

FormData request body​

FormData is a special type of body to construct a set of key-value pairs with variable types of data. A common use case is to upload files to a server.

To send a FormData body, declare its type in your schema. Use the HttpFormData to indicate that the body is a FormData type.

import { HttpFormData, HttpSchema } from '@zimic/http';

interface AvatarFormDataSchema {
image: File;
}

type Schema = HttpSchema<{
'/users/:userId/avatar': {
PUT: {
request: {
headers?: { 'content-type'?: 'multipart/form-data' };
body: HttpFormData<AvatarFormDataSchema>;
};
response: {
200: { body: { url: string } };
};
};
};
}>;
import { HttpFormData } from '@zimic/http';

// Getting an uploaded file from an input element
const imageInput = document.querySelector<HTMLInputElement>('input[type="file"]');
const imageFile = imageInput!.files![0];

const formData = new HttpFormData<AvatarFormDataSchema>();
formData.append('image', imageFile);

interceptor
.post('/users')
.with({
body: formData,
})
.respond({
status: 201,
body: { url: `https://example.com/${user.id}/avatar.png` },
})
.times(1);

Binary request body​

Binary bodies are used to send raw binary data in requests. To send a binary body, declare its type in your schema. Blob, ArrayBuffer, and ReadableStream are common types for binary data.

schema.ts
import { HttpSchema } from '@zimic/http';

interface Video {
id: string;
url: string;
}

type Schema = HttpSchema<{
'/upload': {
POST: {
request: {
headers?: { 'content-type'?: string };
body: Blob;
};
};
response: {
201: { body: Video };
};
};
}>;
import fs from 'fs';

// Getting a file from the file system
const videoBuffer = await fs.promises.readFile('video.mp4');
const videoFile = new File([videoBuffer], 'video.mp4');

interceptor
.post('/upload')
.with({
body: videoFile,
})
.respond({
status: 201,
body: video,
})
.times(1);

Request streaming​

When working with large files or chunked data, you can use ReadableStream to handle streamed request bodies.

schema.ts
import { HttpSchema } from '@zimic/http';

interface Video {
id: string;
url: string;
}

type Schema = HttpSchema<{
'/upload': {
POST: {
request: {
headers?: { 'content-type'?: string };
body: ReadableStream;
};
};
response: {
201: { body: Video };
};
};
}>;
import fs from 'fs';
import { Readable } from 'stream';

// Streaming a file from the file system
const fileStream = fs.createReadStream('video.mp4');
const requestStream = Readable.toWeb(fileStream);

interceptor
.post('/upload')
.with({
body: requestStream,
})
.respond({
status: 201,
body: video,
})
.times(1);

Plain-text request body​

Plain-text bodies can be declared as a string.

import { HttpSchema } from '@zimic/http';

type Schema = HttpSchema<{
'/content': {
POST: {
request: {
body: string;
};
response: {
201: {};
};
};
};
}>;
interceptor
.post('/context')
.with({
body: 'text',
})
.respond({
status: 201,
})
.times(1);

URL-encoded request body​

Bodies with URL-encoded data can be declared with HttpSearchParams.

import { HttpSchema, HttpSearchParams } from '@zimic/http';

interface UserCreationSearchParams {
username: string;
}

type Schema = HttpSchema<{
'/users': {
POST: {
request: {
headers?: { 'content-type'?: 'application/x-www-form-urlencoded' };
body: HttpSearchParams<UserCreationSearchParams>;
};
};
};
}>;
import { HttpSearchParams } from '@zimic/http';

const searchParams = new HttpSearchParams<UserCreationSearchParams>({
username: 'me',
});

interceptor
.post('/users')
.with({
body: searchParams,
})
.respond({
status: 201,
body: user,
})
.times(1);

Using response bodies​

Response bodies are declared in your schema using the response.<status>.body property.

Then, they will be available in the body property, such as in handler.respond().

JSON response body​

To use a JSON response body, declare its type in your schema.

schema.ts
import { HttpSchema } from '@zimic/http';

interface User {
id: string;
username: string;
}

type Schema = HttpSchema<{
'/users': {
GET: {
response: {
200: {
body: User[];
};
};
};
};
}>;
interceptor
.get('/users')
.respond({
status: 200,
body: users,
})
.times(1);

FormData response body​

To use a FormData response body, declare its type in your schema using the HttpFormData.

schema.ts
import { HttpFormData, HttpSchema } from '@zimic/http';

interface AvatarFormDataSchema {
image: File;
}

type Schema = HttpSchema<{
'/users/:userId/avatar': {
GET: {
response: {
200: {
headers?: { 'content-type'?: 'multipart/form-data' };
body: HttpFormData<AvatarFormDataSchema>;
};
};
};
};
}>;
import { HttpFormData } from '@zimic/http';

const formData = new HttpFormData<AvatarFormDataSchema>();
formData.append('image', new File([''], 'avatar.png'));

interceptor
.get(`/users/${user.id}/avatar`)
.respond({
status: 200,
body: formData,
})
.times(1);

Binary response body​

Binary response bodies can be declared with types such as Blob, ArrayBuffer, and ReadableStream.

schema.ts
import { HttpSchema } from '@zimic/http';

interface Video {
id: string;
url: string;
}

type Schema = HttpSchema<{
'/videos/:videoId': {
GET: {
response: {
200: {
headers?: { 'content-type'?: string };
body: Blob;
};
};
};
};
}>;
import fs from 'fs';
import { HttpFormData } from '@zimic/http';

// Getting a file from the file system
const videoBuffer = await fs.promises.readFile('video.mp4');
const videoFile = new Blob([videoBuffer], { type: 'video/mp4' });

interceptor
.get(`/videos/${video.id}`)
.respond({
status: 200,
body: videoFile,
})
.times(1);

Response streaming​

Similarly to request streaming, you can stream response bodies using ReadableStream.

schema.ts
import { HttpSchema } from '@zimic/http';

type Schema = HttpSchema<{
'/videos/:videoId': {
GET: {
response: {
200: {
headers?: { 'content-type'?: string };
body: ReadableStream;
};
};
};
};
}>;
import fs from 'fs';
import stream, { Readable } from 'stream';
import { HttpFormData } from '@zimic/http';

// Streaming a file from the file system
const fileStream = fs.createReadStream('video.mp4');
const responseStream = Readable.toWeb(fileStream);

interceptor
.get(`/videos/${video.id}`)
.respond({
status: 200,
body: responseStream,
})
.times(1);

Plain-text response body​

Plain-text response bodies can be declared as a string.

schema.ts
import { HttpSchema } from '@zimic/http';

type Schema = HttpSchema<{
'/content': {
GET: {
response: {
200: {
body: string;
};
};
};
};
}>;
interceptor
.get('/content')
.respond({
status: 200,
body: 'text',
})
.times(1);

URL-encoded response body​

URL-encoded response bodies can be declared with HttpSearchParams.

schema.ts
import { HttpSchema, HttpSearchParams } from '@zimic/http';

interface UserCreationSearchParams {
username: string;
}

type Schema = HttpSchema<{
'/users': {
GET: {
response: {
200: {
headers?: { 'content-type'?: 'application/x-www-form-urlencoded' };
body: HttpSearchParams<UserCreationSearchParams>;
};
};
};
};
}>;
import { HttpSearchParams } from '@zimic/http';

const searchParams = new HttpSearchParams<UserCreationSearchParams>({
username: 'me',
});

interceptor
.get('/users')
.respond({
status: 200,
body: searchParams,
})
.times(1);