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-type | Parsed as |
|---|---|
application/json | JSON (object) |
application/xml | string |
application/x-www-form-urlencoded | HttpSearchParams |
application/* (others) | Blob |
multipart/form-data | HttpFormData |
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.
import { HttpSchema } from '@zimic/http';
interface User {
id: string;
username: string;
}
type Schema = HttpSchema<{
'/users': {
POST: {
request: {
body: { username: string };
};
response: {
201: { body: User };
};
};
};
}>;
- Local interceptor
- Remote interceptor
interceptor
.post('/users')
.with({
body: { username: 'me' },
})
.respond({
status: 201,
body: user,
})
.times(1);
await interceptor
.post('/users')
.with({
body: { username: 'me' },
})
.respond({
status: 201,
body: user,
})
.times(1);
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.
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 } };
};
};
};
}>;
- Local interceptor
- Remote interceptor
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);
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);
await 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.
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 };
};
};
}>;
- Local interceptor
- Remote interceptor
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);
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');
await 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.
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 };
};
};
}>;
- Local interceptor
- Remote interceptor
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);
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);
await 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: {};
};
};
};
}>;
- Local interceptor
- Remote interceptor
interceptor
.post('/context')
.with({
body: 'text',
})
.respond({
status: 201,
})
.times(1);
await 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>;
};
};
};
}>;
- Local interceptor
- Remote interceptor
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);
import { HttpSearchParams } from '@zimic/http';
const searchParams = new HttpSearchParams<UserCreationSearchParams>({
username: 'me',
});
await 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.
import { HttpSchema } from '@zimic/http';
interface User {
id: string;
username: string;
}
type Schema = HttpSchema<{
'/users': {
GET: {
response: {
200: {
body: User[];
};
};
};
};
}>;
- Local interceptor
- Remote interceptor
interceptor
.get('/users')
.respond({
status: 200,
body: users,
})
.times(1);
await 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.
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>;
};
};
};
};
}>;
- Local interceptor
- Remote interceptor
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);
import { HttpFormData } from '@zimic/http';
const formData = new HttpFormData<AvatarFormDataSchema>();
formData.append('image', new File([''], 'avatar.png'));
await 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.
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;
};
};
};
};
}>;
- Local interceptor
- Remote interceptor
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);
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' });
await 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.
import { HttpSchema } from '@zimic/http';
type Schema = HttpSchema<{
'/videos/:videoId': {
GET: {
response: {
200: {
headers?: { 'content-type'?: string };
body: ReadableStream;
};
};
};
};
}>;
- Local interceptor
- Remote interceptor
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);
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);
await 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.
import { HttpSchema } from '@zimic/http';
type Schema = HttpSchema<{
'/content': {
GET: {
response: {
200: {
body: string;
};
};
};
};
}>;
- Local interceptor
- Remote interceptor
interceptor
.get('/content')
.respond({
status: 200,
body: 'text',
})
.times(1);
await interceptor
.get('/content')
.respond({
status: 200,
body: 'text',
})
.times(1);
URL-encoded response body
URL-encoded response bodies can be declared with HttpSearchParams.
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>;
};
};
};
};
}>;
- Local interceptor
- Remote interceptor
import { HttpSearchParams } from '@zimic/http';
const searchParams = new HttpSearchParams<UserCreationSearchParams>({
username: 'me',
});
interceptor
.get('/users')
.respond({
status: 200,
body: searchParams,
})
.times(1);
import { HttpSearchParams } from '@zimic/http';
const searchParams = new HttpSearchParams<UserCreationSearchParams>({
username: 'me',
});
await interceptor
.get('/users')
.respond({
status: 200,
body: searchParams,
})
.times(1);