Develop the app using file-based routing
mainapp directory.repository·main·Indexed 19 days ago
https://github.com/suhaotian/xiorA lightweight (~6KB) HTTP request library built on the native fetch API, offering a developer experience similar to Axios. It features strong TypeScript typing, support for interceptors, and a plugin system for functionality such as error retries, request throttling, deduplication, caching, and token refresh. xior is optimized for modern environments including Cloudflare Workers, Next.js, Vercel Edge Functions, and Expo.
app directory.If you need full control over the build tool and configuration (webpack, Babel, ESLint, etc.), you can run npm run eject.
Warning: This is a one-way operation. Once you eject, you cannot go back.
Ejecting copies all configuration files and transitive dependencies directly into your project. While all other commands will still work, they will point to the newly copied scripts, and you will be responsible for maintaining the configuration.
npm run ejectBy default, xior parses the response body (e.g., into JSON). If you need to access the raw response object (for example, to use a ReadableStream for line-by-line text processing), set the responseType option to 'original'. This returns an object in the format { response } where response is the original response object.
import xior from 'xior';
const http = xior.create();
async function* makeTextFileLineIterator(fileURL) {
const utf8Decoder = new TextDecoder('utf-8');
// Use responseType: 'original' to get the raw response object
const { response } = await http.get(fileURL, { responseType: 'original' });
const reader = response.body.getReader();
// ... rest of the stream processing logic
}You can control what happens when a request does not match any registered mock handler using the onNoMatch option in the MockPlugin constructor.
onNoMatch: 'passthrough': All unmatched requests are automatically forwarded to the actual network..passThrough(): You can also call .passThrough() on a specific handler to force matched requests to go to the network instead of being intercepted.// Option 1: Global passthrough for all unmatched requests
const mock = new MockPlugin(instance, { onNoMatch: 'passthrough' });
// Option 2: Specific handler passthrough
mock.onGet('/api/real-data').passThrough();Because Xior.js uses the Fetch API's Headers object for responses, you cannot access headers using bracket notation like a standard JavaScript object. You must use the .get() method on the headers property of the response object.
// In Axios:
const value = response.headers['x-header-name'];
// In Xior.js:
const value = response.headers.get('x-header-name');xior uses a plugin mechanism to extend its functionality. Plugins are registered using http.plugins.use().
Crucial Execution Order: The plugin mechanism follows a 'first in, last run' pattern. If you register plugins in the order plugin1, plugin2, plugin3, they will execute in the order plugin3 $\rightarrow$ plugin2 $\rightarrow$ plugin1 during the request lifecycle.
plugins.use(plugin1);
plugins.use(plugin2);
plugins.use(plugin3);
// Run order: plugin3 → plugin2 → plugin1Since xior does not support transformRequest or transformResponse properties, use these alternatives:
interceptors.request.use to modify the config object (e.g., changing headers or data) before the request is sent..then() callback to transform the response data after it is received.// Request transformation via interceptor
xiorInstance.interceptors.request.use((config) => {
if (config.url === '/endpoint') {
delete config.headers['Content-Type'];
}
return config;
});
// Response transformation via .then()
xiorInstance.get('/api').then((response) => {
// transform response data here
return response;
});For POST requests, xior allows you to pass the request body directly as the second argument. You can also pass a configuration object as the third argument to specify mode, cache, credentials, headers, redirect, and referrerPolicy, mirroring the native fetch options.
import xior from 'xior';
const http = xior.create({
baseURL: 'http://example.com',
});
http
.post(
'/answer',
{ answer: 42 },
{
mode: 'cors', // no-cors, *cors, same-origin
cache: 'no-cache', // *default, no-cache, reload, force-cache, only-if-cached
credentials: 'same-origin', // include, *same-origin, omit
headers: {
// 'Content-Type': 'application/json',
},
redirect: 'follow', // manual, *follow, error
referrerPolicy: 'no-referrer', // no-referrer, *no-referrer-when-downgrade, origin, origin-when-cross-origin, same-origin, strict-origin, strict-origin-when-cross-origin, unsafe-url
}
)
.then(({ data }) => {
console.log(data);
});xior within a Next.js environment, specifically ensuring compatibility with Vercel Edge Functions. The project provides implementations for both standard Node.js runtime APIs and the more restricted Vercel Edge runtime, which is useful for low-latency edge computing scenarios.When migrating from the native fetch API to xior, you can replace manual URL string concatenation and .json() parsing with xior.create() and the params option. xior automatically extracts the data property from the response, which contains the parsed JSON body.
import xior from 'xior';
const http = xior.create({
baseURL: 'http://example.com',
});
async function logMovies() {
const { data: movies } = await http.get('/movies.json', {
params: {
page: 1,
perPage: 10,
},
});
console.log(movies);
}By default, xior attempts to parse response text as JSON. To implement custom parsing logic (e.g., based on Content-Type), set responseType: 'custom' and use a response interceptor.
import xior from 'xior';
const http = xior.create({
baseURL,
responseType: 'custom', // Disables default parsing
});
const typeMatchers = [
['json', [/^application\/.*json$/, /^$/]],
['text', [/^text\//, /^image\/svg\+xml$/, /^application\/.*xml$/]],
] as const;
http.interceptors.response.use(async (res) => {
if (res.config.responseType !== 'custom') return res;
const { response } = res;
const headers = response?.headers;
if (!response || headers.get('Content-Length') === '0') return res;
const contentType = headers.get('Content-Type')?.split(';')?.[0]?.trim() || '';
const matchedType = typeMatchers.find(([_, patterns]) =>
patterns.some((pattern) => pattern.test(contentType))
);
if (matchedType) {
const [method] = matchedType;
res.data = await response[method]();
}
return res;
});const http = xior.create({ responseType: 'custom' });Use the FormData API for file uploads. To track upload progress, use the xior/plugins/progress plugin.
import Xior from 'xior';
import uploadDownloadProgressPlugin from 'xior/plugins/progress';
const http = Xior.create({});
// Register the progress plugin
http.plugins.use(
uploadDownloadProgressPlugin({
progressDuration: 5 * 1000,
})
);
const formData = new FormData();
formData.append('file', fileObject);
formData.append('field1', 'val1');
http.post('/upload', formData, {
onUploadProgress(e) {
console.log(`Upload progress: ${e.progress}%`);
},
});import uploadDownloadProgressPlugin from 'xior/plugins/progress';
// ... setup http instance
http.plugins.use(uploadDownloadProgressPlugin());