A unified request client based on axios.
vp add @liofelix/unified-request axiosimport axios from "axios";
import { createRequestClient, type RequestClientOptions } from "@liofelix/unified-request";
const options: RequestClientOptions = {
axiosConfig: {
baseURL: "https://example.com/api",
},
interceptor: {
request: [
(config) => {
config.headers.set("x-request-id", crypto.randomUUID());
return config;
},
],
response: [
(response) => response,
(error) => {
if (axios.isAxiosError(error) && error.response?.status === 401) {
// remove token, redirect to login, etc.
}
console.error(error);
return Promise.reject(error);
},
],
},
};
const client = createRequestClient(options);
const response = await client.get("/health");
console.log(response.data);function createRequestClient(options?: RequestClientOptions): RequestClient;
interface RequestClientOptions {
axiosConfig?: CreateAxiosDefaults;
interceptor?: RequestClientInterceptor;
}createRequestClient returns the axios instance directly. Response interceptors
keep the axios response shape by default, so callers continue reading business
data from response.data.
All axios instance options should be passed through axiosConfig:
createRequestClient({
axiosConfig: {
baseURL: "https://example.com/api",
timeout: 3000,
},
});Use the second item of interceptor.response for centralized error handling,
including 401 responses. createRequestClient accepts one shared request/response
interceptor pair.
If an application needs additional interceptors, register them directly on the returned axios instance:
const client = createRequestClient(options);
client.interceptors.request.use((config) => config);
client.interceptors.response.use((response) => response);The 2026-06-24 interceptor API replaces the old top-level request options with
regular axios configuration and interceptors. Move axios instance options into
axiosConfig, then move onUnauthorized and onError logic into the rejected
response interceptor at interceptor.response[1].
Before:
createRequestClient({
baseURL: "https://example.com/api",
onUnauthorized: () => {
// remove token, redirect to login, etc.
},
onError: (error) => {
console.error(error);
},
});After:
import axios from "axios";
createRequestClient({
axiosConfig: {
baseURL: "https://example.com/api",
},
interceptor: {
response: [
(response) => response,
(error) => {
if (axios.isAxiosError(error) && error.response?.status === 401) {
// remove token, redirect to login, etc.
}
console.error(error);
return Promise.reject(error);
},
],
},
});- Move axios options such as
baseURL,timeout,headers, andadapterfrom the top level intoaxiosConfig. - Move 401 handling from
onUnauthorizedinto theinterceptor.response[1]401 branch. - Move shared error handling from
onErrorinto the same rejected response interceptor. - Return
Promise.reject(error)from the rejected response interceptor whenever the centralized handler does not recover from the error.
The external interceptor option now accepts one object. Replace the old
interceptors array with the singular interceptor option:
// Before
createRequestClient({
interceptors: [{ request, response }],
});
// After
createRequestClient({
interceptor: { request, response },
});There is no compatibility layer for the old array option. Register any additional
interceptors with client.interceptors.request.use() or
client.interceptors.response.use() after creating the client.
The package exports these types:
RequestClientRequestClientOptionsRequestClientInterceptorRequestInterceptorResponseInterceptor
- Install dependencies:
vp install- Run the unit tests:
vp test- Build the library:
vp pack