Skip to main content

Client API instance

Use Zodios instance to fetch data from multiple API endpoints.
It's an API client based on zod and axios with a powerful plugin system.

Zodios API client is like no one at the moment to make REST API fetching.

All your parameters and responses are fully typed. And by default they are also validated at runtime to prevent unrecoverable errors.
Check the simple API definition format that powers full end-to-end typesafety.

Create Zodios Client instance

When creating an instance or zodios api client, you need to at least provide the api definition.
baseURL is optional in browsers and will default to the current page url.

new Zodios(baseURL: string, api: ZodiosEnpointDescriptions, options?: ZodiosOptions)
// or
new Zodios(api: ZodiosEnpointDescriptions, options?: ZodiosOptions)

Example

You can predefine some schemas to reuse them in your API definition.

import { Zodios, makeErrors } from "@zodios/core";
import z from "zod";

const errors = makeErrors([
{
status: "default",
schema: z.object({
error: z.object({
code: z.number(),
message: z.string(),
}),
}),
},
]);

const user = z.object({
id: z.number(),
name: z.string(),
age: z.number().positive(),
email: z.string().email(),
});

Then you can define your API endpoints directly in the Zodios constructor.

const apiClient = new Zodios('/api', [
{
method: "get",
path: "/users",
alias: "getUsers",
response: z.array(user),
},
{
method: "get",
path: "/users/:id",
alias: "getUser",
response: user,
errors,
},
{
method: "post",
path: "/users",
alias: "createUser",
parameters: [
{
name: "user",
type: "Body",
schema: user.omit({ id: true }),
},
],
response: user,
errors,
},
]);

And finally you can use it to fetch data from your API.

// get all users
const users = await apiClient.getUsers();
// get user by id
const user = await apiClient.getUser({ params: { id: 1 } });
// create user
const newUser = await apiClient.createUser({ name: "John", age: 20, email: "jodn@doe.com"});
Path parameters do not need to be defined in the API definition parameters array.

Indeed, they are automatically deduced from the path and added to the request parameters implicitly.

Options

Zodios API client constructor options ZodiosOptions are straightforward.

OptionTypeDefaultDescription
validateboolean | all | none | request | responsetrueValidate parameters and responses at runtime.
transformboolean | request | responsetrueTransform parameters and responses at runtime.
sendDefaultsbooleanfalseSend default values on parameters when provided on schema. You usually want your server to set the default values, not your client
axiosInstanceAxiosInstanceaxios.create()add your own axios instance
axiosConfigAxiosRequestConfig{}add your own default axios config
transform implies validation

In order to transform data, zod needs to parse the data successfully.

Zodios attributes

baseURL

access baseURL property to get the base url of the api.

axios

access axios property to get back zodios internal axios instance.

Zodios methods

zodios.use

use method allows to add a plugin to the client instance. See plugins for more information.

use(plugin: ZodiosPlugin): PluginId;
// or
use(method: string, path: string, plugin: ZodiosPlugin): PluginId;
// or
use(alias: string, plugin: ZodiosPlugin): PluginId;

Example:

import { pluginFetch } from "@zodios/plugins";

apiClient.use(pluginFetch({
keepAlive: true,
}));

zodios.eject

Eject method allows to remove a plugin from the client instance.

eject(pluginId: PluginId): void;

zodios.[alias]

You will usually want to use aliases to call your endpoints. You can define them in the alias option in your API definition endpoint.

query alias:

function [alias](config?: ZodiosRequestOptions): Promise<Response>;
note

For more information about ZodiosRequestOptions see request options You don't need to declare path parameters in the parameters array of the API definition. Just remember you can use params to pass path parameters and queries to pass query parameters. See examples below.

example:

// identical to api.get("/users")
const users = await api.getUsers();

with path parameters

// identical to api.get("/users", { params: { id: 1 } })
const user = await api.getUser({ params: { id: 1 } }); // GET /users/1

with query parameters

// identical to api.get("/users", { queries: { limit: 10 } })
const users = await api.getUsers({ queries: { limit: 10 } }); // GET /users?limit=10

mutation alias

Alias for post, put, patch, delete endpoints:

function [alias](body: BodyParam, config?: ZodiosRequestOptions): Promise<Response>;
note

For more information about ZodiosRequestOptions see request options You don't need to declare path parameters in the parameters array of the API definition. Just remember you can use params to pass path parameters and queries to pass query parameters. See examples below.

example:

// identical to api.post("/users", { name: "John" })
const user = await api.createUser({ name: "John" });

Is equivalent to the following HTTP request:

POST /users HTTP/1.1
Content-Type: application/json

{
"name": "John"
}

zodios.request

Generic request method that allows to do both query and mutation calls.

request(config: ZodiosRequestOptions): Promise<Response>;
note

For more information about ZodiosRequestOptions see request options You don't need to declare path parameters in the parameters array of the API definition. Just remember you can use params to pass path parameters and queries to pass query parameters. See examples below.

Example:

const user = await api.request({
method: "post",
url: "/users",
data: { name: "John" },
});

zodios.get

get(path: string, config?: ZodiosRequestOptions): Promise<Response>;
note

For more information about ZodiosRequestOptions see request options You don't need to declare path parameters in the parameters array of the API definition. Just remember you can use params to pass path parameters and queries to pass query parameters. See examples below.

example:

const users = await api.get("/users");

with path parameters

const user = await api.get("/users/:id", { params: { id: 1 } }); // GET /users/1

with query parameters

const users = await api.get("/users", { queries: { limit: 10 } }); // GET /users?limit=10

zodios.post

post(path: string, body: BodyParam, config?: ZodiosRequestOptions): Promise<Response>;
note

For more information about ZodiosRequestOptions see request options You don't need to declare path parameters in the parameters array of the API definition. Just remember you can use params to pass path parameters and queries to pass query parameters. See examples below.

Example:

const user = await api.post("/users", { name: "John" });

Is equivalent to the following HTTP request:

POST /users HTTP/1.1
Accept: application/json
Content-Type: application/json

{
"name": "John"
}

zodios.put

put(path: string, body: BodyParam, config?: ZodiosRequestOptions): Promise<Response>;
note

For more information about ZodiosRequestOptions see request options You don't need to declare path parameters in the parameters array of the API definition. Just remember you can use params to pass path parameters and queries to pass query parameters. See examples below.

Example:

const user = await api.put("/users/:id", {id: 1, name: "John" }, { params: { id: 1 } });

will send the following HTTP request:

PUT /users/1 HTTP/1.1
Accept: application/json
Content-Type: application/json

{
"id": 1,
"name": "John"
}

zodios.patch

patch(path: string, body: BodyParam, config?: ZodiosRequestOptions): Promise<Response>;
note

For more information about ZodiosRequestOptions see request options You don't need to declare path parameters in the parameters array of the API definition. Just remember you can use params to pass path parameters and queries to pass query parameters. See examples below.

Example:

const user = await api.patch("/users/:id", {name: "John" }, {params: {id: 1}});

will send the following HTTP request:

PATCH /users/1 HTTP/1.1
Accept: application/json
Content-Type: application/json

{
"name": "John"
}

zodios.delete

delete(path: string, body: BodyParam, config?: ZodiosRequestOptions): Promise<Response>;
note

For more information about ZodiosRequestOptions see request options You don't need to declare path parameters in the parameters array of the API definition. Just remember you can use params to pass path parameters and queries to pass query parameters. See examples below.

Example:

const user = await api.delete("/users/:id", undefined, {params: {id: 1}});

will send the following HTTP request:

DELETE /users/1 HTTP/1.1
Accept: application/json

Request Options

propertytypedescription
method"get" | "post" | "put" | "patch" | "delete"HTTP method
urlstringpath to the endpoint
baseURLstringoptional base url of the API, use it to override the provided one
dataBodyParamoptional request body
paramsRecord<string, string | number>optional path parameters
queriesRecord<string, string | number | string[] | number[]>optional query parameters
headersRecord<string, string>optional request headers
signalAbortSignaloptional AbortSignal
paramsSerializer(params: QueryParams) => stringoptional function to serialize query parameters
timeoutnumberrequest timeout, default is 0, no timeout
withCredentialsbooleanflag to enable credentials, default is false
adapterAxiosAdapteroptional custom adapter, let libray authors implement it
auth{ username: string; password: string }optional basic auth
responseType"arraybuffer" | "blob" | "document" | "json" | "text" | "stream"response type, default is "json"
responseEncodingstringresponse encoding, default is "utf8" (nodejs only)
xsrfCookieNamestringxsrf cookie name, default is "XSRF-TOKEN"
xsrfHeaderNamestringxsrf header name, default is "X-XSRF-TOKEN"
onUploadProgress(progressEvent: ProgressEvent) => voidprogress callback (browser only with XMLHttpRequest)
onDownloadProgress(progressEvent: ProgressEvent) => voidprogress callback (browser only with XMLHttpRequest)
maxContentLengthnumbermax response content length, default is 2000 bytes
maxBodyLengthnumbermax request body length, default is 2000 bytes
validateStatus(status: number) => booleanoptional function to validate status code
maxRedirectsnumbermax number of redirects, default is 5
httpAgentNodeHttpAgentoptional custom http agent (nodejs only)
httpsAgentNodeHttpsAgentoptional custom https agent (nodejs only)
proxy{ protocol: string; host: string; port: number; auth?: { username: string; password: string } }optional proxy configuration (nodejs only)
decompressbooleanflag to enable automatic decompression, default is true

Advanced examples

Use zod transformations

Since Zodios is using zod, you can use zod transformations.

const apiClient = new Zodios(
"https://jsonplaceholder.typicode.com",
[
{
method: "get",
path: "/users/:id",
alias: "getUser",
description: "Get a user",
response: z.object({
id: z.number(),
name: z.string(),
}).transform(({ name,...rest }) => ({
...rest,
firstname: name.split(" ")[0],
lastname: name.split(" ")[1],
})),
},
]
);

const user = await apiClient.getUser({ params: { id: 7 } });

console.log(user);

It should output

{ id: 7, firstname: 'Kurtis', lastname: 'Weissnat' }

Send multipart/form-data requests

Zodios supports multipart/form-data requests with integrated requestFormat.

node

If you are using Zodios on your backend node, you need to install form-data package to use multipart/form-data requests. And polyfill FormData with globalThis.FormData = require("form-data"); before importing Zodios.

const apiClient = new Zodios(
"https://mywebsite.com",
[{
method: "post",
path: "/upload",
alias: "upload",
description: "Upload a file",
requestFormat: "form-data",
parameters:[
{
name: "body",
type: "Body",
schema: z.object({
file: z.instanceof(File),
}),
}
],
response: z.object({
id: z.number(),
}),
}],
);
const id = await apiClient.upload({ file: document.querySelector('#file').files[0] });

But you can also use your own multipart/form-data library, for example with form-data library on node.

import FormData from 'form-data';

const apiClient = new Zodios(
"https://mywebsite.com",
[{
method: "post",
path: "/upload",
alias: "upload",
description: "Upload a file",
parameters:[
{
name: "body",
type: "Body",
schema: z.instanceof(FormData),
}
],
response: z.object({
id: z.number(),
}),
}],
);
const form = new FormData();
form.append('file', document.querySelector('#file').files[0]);
const id = await apiClient.upload(form, { headers: form.getHeaders() });

Send application/x-www-form-urlencoded requests

Zodios supports application/x-www-form-urlencoded requests with integrated requestFormat. Zodios is using URLSearchParams internally on both browser and node. (If you need IE support, see next example)

const apiClient = new Zodios(
"https://mywebsite.com",
[{
method: "post",
path: "/login",
alias: "login",
description: "Submit a form",
requestFormat: "form-url",
parameters:[
{
name: "body",
type: "Body",
schema: z.object({
userName: z.string(),
password: z.string(),
}),
}
],
response: z.object({
id: z.number(),
}),
}],
);
const id = await apiClient.login({ userName: "user", password: "password" });

But you can also use custom code to support for application/x-www-form-urlencoded requests. For example with qs library on IE :

import qs from 'qs';

const apiClient = new Zodios(
"https://mywebsite.com",
[{
method: "post",
path: "/login",
alias: "login",
description: "Submit a form",
parameters:[
{
name: "body",
type: "Body",
schema: z.object({
userName: z.string(),
password: z.string(),
}).transform(data=> qs.stringify(data)),
}
],
response: z.object({
id: z.number(),
}),
}],
);
const id = await apiClient.login({ userName: "user", password: "password" },
{ headers:
{
'Content-Type': 'application/x-www-form-urlencoded'
}
});

CRUD helper

Zodios has a helper to generate basic CRUD API. It will generate all the api definitions for you :

import { Zodios, makeCrudApi } from '@zodios/core';

const apiClient = new Zodios(BASE_URL,
makeCrudApi(
'user',
z.object({
id: z.number(),
name: z.string(),
})
));

Is the same as :

const apiClient = new Zodios(BASE_URL, [
{
method: "get",
path: "/users",
alias: "getUsers",
description: "Get all users",
response: z.array(userSchema),
},
{
method: "get",
path: "/users/:id",
alias: "getUser",
description: "Get a user",
response: userSchema,
},
{
method: "post",
path: "/users",
alias: "createUser",
description: "Create a user",
parameters: [
{
name: "body",
type: "Body",
description: "The object to create",
schema: userSchema.partial(),
},
],
response: userSchema,
},
{
method: "put",
path: "/users/:id",
alias: "updateUser",
description: "Update a user",
parameters: [
{
name: "body",
type: "Body",
description: "The object to update",
schema: userSchema,
},
],
response: userSchema,
},
{
method: "patch",
path: "/users/:id",
alias: "patchUser",
description: "Patch a user",
parameters: [
{
name: "body",
type: "Body",
description: "The object to patch",
schema: userSchema.partial(),
},
],
response: userSchema,
},
{
method: "delete",
path: "/users/:id",
alias: "deleteUser",
description: "Delete a user",
response: userSchema,
},
]);