Open-source project
suhaotian/xior avatar
suhaotian/xior

Xior: an axios-shaped fetch wrapper with a plugin pipeline

A liteweight fetch wrapper with plugins support and similar API to axios.

456 stars10 forksTypeScriptMIT

At a glance

What is it?
Xior wraps the platform fetch API in an axios-style interface and adds a plugin layer for retries, deduplication, throttling and caching. It suits projects that want axios ergonomics without axios weight, but the plugin set is where the real design decisions live.
Who is it for?
Adopt xior if you are starting a browser or edge project that already assumes fetch and you want axios-shaped calls plus retry, dedupe or cache behaviour without pulling in a second HTTP stack. Skip it if you need axios's full surface, its Node adapter behaviour, or a request library that is not tied to fetch semantics.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 22 days ago.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 25, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The problem xior solves for fetch-first codebases

Fetch is in every modern runtime, but it is a low-level primitive. You get a promise for a Response, and then you write the same twenty lines again in every project: check the status, parse the body according to content type, throw on non-2xx, wire an AbortController for timeouts, and repeat that for every call site. Axios solved this years ago, but it ships its own HTTP machinery and its own adapter layer, which is weight you do not need if fetch is already present.

Xior takes the middle position. The README describes it as a lightweight HTTP request library based on fetch with plugin support and similar API to axios. Concretely, that means xior.create, xior.interceptors, and the method set .get/.post/.put/.patch/.delete/.head/.options. If you have written axios code, the call shapes transfer almost directly. The README even includes a dedicated migrate-axios-to-xior.md file at the repository root, alongside a set of example apps (next-example, vue-cli-app, expo-app, bun-example, cloudflare-example).

The audience is narrower than "everyone who makes HTTP calls". It is people who have already decided fetch is their transport and want the ergonomics on top, plus a plugin pipeline they can extend. If you have never used axios, the value proposition is weaker, because there is no muscle memory to preserve.

How the plugin pipeline and interceptors fit together

The README lists the plugin set explicitly: error retry, deduplication, throttling, cache, error cache, mock, and custom plugins. Two auth refresh token plugins are also listed, one marked as from community and one built in. Each plugin is a separate entry point in the exports map of package.json, so a bundler that respects sideEffects: false can drop the ones you never import. The listed entries include ./plugins/error-retry, ./plugins/throttle, ./plugins/dedupe, and the package also exposes ./utils.

That per-plugin subpath layout is the most interesting architectural choice here. A cache plugin and a retry plugin have very different cost profiles, and forcing both on every consumer would undercut the roughly 6KB (about 3KB gzipped) size the README claims. Splitting them means you pay for what you register.

Interceptors sit alongside plugins rather than replacing them. The README documents using interceptors and cleaning them up, which implies they are registered per instance and removable. Plugins appear to operate at a different layer, closer to the request lifecycle than to a single request or response transform. The README does not publish a diagram of that ordering, so if the relative execution order of an interceptor and a plugin matters to your logic, that is something to determine from the source rather than the docs.

The exports map also distinguishes browser, react-native, worker, and node conditions, with index-node as the default. That is a real signal about intent: the same package targets server rendering, workers and native app runtimes, not just a browser tab.

Installing xior and making the first request

The README gives four package manager commands. Any of them works; the package name is xior in all cases.

bash
npm install xior

pnpm add xior, bun add xior and yarn add xior are listed as equivalents. There is also a UMD build for plain script tags, served from jsDelivr or unpkg at dist/xior.umd.js for version 0.8.4, which exposes a global xior object with a VERSION property.

The first real use is creating an instance with a base URL and then calling a method on it. The README's example looks like this.

ts
import xior from 'xior';

export const xiorInstance = xior.create({
  baseURL: 'https://apiexampledomain.com/api',
  headers: {
    // put your common custom headers here
  },
});

From that instance you call xiorInstance.get('/') and destructure data from the result. The README shows params being passed as an object and notes that nested query encoding is supported, so params: { a: 1, b: 2, c: { d: 1 } } is a documented shape. Headers can be set per request, and the method is generic: xiorInstance.get<{ field1: string; field2: number }>('/') types the returned data.

Defaults are mutable after creation. The README shows setting xiorInstance.defaults.headers['Authorization'] to a bearer token and deleting the same key to remove it, with a commented-out equivalent for defaults.params. That is the pattern to use for login and logout flows rather than recreating the instance.

Where xior stops being the right tool

The README's first FAQ entry is whether xior is 100 percent compatible with axios. The existence of that question, and the separate migration document, tells you the answer is no. The migration guide is organized as a list of individual translations: GET, POST, the axios(requestObj) form, creating an instance, reading response headers, transformRequest, transformResponse, and downloading with responseType set to stream or blob. Each of those is a place where behaviour differs enough to need a written mapping.

The transformRequest and transformResponse entries are the sharpest edge. Axios has a specific transform pipeline with its own defaults, and xior's README treats both as migration topics rather than as drop-in equivalents. If your codebase relies on axios transforms for serialization, budget time for that translation rather than assuming it is mechanical.

There is also a structural limitation that comes with the fetch foundation. The README has a FAQ on using a custom fetch implementation and on proxy support, phrased as one question. In other words, proxying is not a first-class feature of xior; it is something you get by supplying a fetch implementation that already handles it. Node users who expect the axios-style proxy configuration object will not find it here.

Finally, the project is a small library with a single maintainer and a plugin surface that includes community contributions. That is not a defect, but it does mean the retry, cache and dedupe plugins are the parts most likely to have edge cases you discover in production rather than in the README.

Xior compared with ofetch and plain fetch wrappers

The closest comparison point in the related search data is ofetch, the unjs fetch wrapper. The two solve the same base problem, so the difference is in the shape of the API and the extension model.

Ofetch's identity is tied to the unjs ecosystem and to Nuxt, where it is the default HTTP client. Its API is function-first: you call ofetch(url, options) and get parsed data back directly, with the response object available through options. Xior is instance-first and axios-first. You create an instance with xior.create, you call methods on it, and you receive a response object with a data property. If your team's mental model is axios, xior matches it; if your mental model is a single function call, ofetch matches it.

The plugin story is the other divergence. Xior's README devotes a large section to named plugins with their own subpath exports: error retry, throttle, dedupe, cache, error cache, mock, and the two auth refresh token plugins. Ofetch's interceptor model is built around onRequest, onRequestError, onResponse and onResponseError hooks. Those are different bets. Xior bets that common cross-cutting concerns deserve packaged, separately importable implementations. The hook model bets that you want to write the logic yourself and just need the seams.

Neither bet is obviously correct. Packaged plugins save you from writing a retry loop with backoff and a dedupe key function, but they also mean you inherit someone else's decisions about what counts as a duplicate request. Hooks give you full control and zero help.

Maintenance, licence and what upgrading costs

The repository is not archived, and the last push was on 2026-08-04. That date coincides with the v0.8.4 release. Before that, v0.8.3 landed on 2026-01-14 and v0.8.2 on 2025-12-19. The gap between v0.8.3 and v0.8.4 is roughly seven months, so the release cadence is not fast, but the project has not gone quiet either. Treat the version line as still moving rather than as frozen.

The version numbers themselves are worth noting. The published package.json in the repository reads 0.8.5 while the most recent release is v0.8.4, and the README's CDN examples pin 0.8.4. Everything is still on a 0.x line, which in semver terms means minor versions are where breaking changes are allowed to land. If you pin xior in a lockfile, read the CHANGELOG.md at the repository root before moving between minors rather than assuming compatibility.

The licence is MIT. That is permissive: it allows commercial use, modification and redistribution provided the copyright notice and permission notice are retained. It does not grant patent rights and it comes with no warranty. None of that is legal advice; if your organisation has a policy on dependency licences, the file to hand to that process is LICENSE at the repository root.

Upgrade cost is dominated by the plugin subpaths. Because each plugin is its own export entry, a breaking change inside one plugin does not force a change in the core, but it also means the changelog is the only place where those changes are collected. The repository also carries a .husky directory and a scripts directory, so there is tooling around releases, but the README does not document a deprecation policy or a rollback procedure.

Editorial conclusion

Adopt xior if you are starting a browser or edge project that already assumes fetch and you want axios-shaped calls plus retry, dedupe or cache behaviour without pulling in a second HTTP stack. Skip it if you need axios's full surface, its Node adapter behaviour, or a request library that is not tied to fetch semantics. Before committing, verify three things yourself: that your runtime's fetch implementation is the one you expect, that the specific plugin you need is exported from the subpath listed in package.json (the throttle entry point in that file contains a typo in its types path, so check the built output), and that your bundler resolves the browser condition in the exports map rather than the node one.

Frequently asked questions

Is xior 100 percent compatible with axios?

No. The README's own FAQ asks this question, and the repository ships a separate migrate-axios-to-xior.md document that walks through individual translations such as transformRequest, transformResponse, and responseType for stream and blob downloads. The API is described as similar to axios, not identical.

How do I install xior?

The README lists npm install xior, pnpm add xior, bun add xior and yarn add xior. A UMD build is also available from jsDelivr or unpkg at dist/xior.umd.js for version 0.8.4, which exposes a global xior object.

Does xior support proxies or a custom fetch implementation?

The README treats these as one FAQ entry, which means proxy support is not a built-in configuration object. You supply a custom fetch implementation that already handles proxying, and xior uses it as the transport.

What plugins does xior ship with?

The README lists error retry, request throttle, request dedupe, cache, error cache, mock, and custom plugins, plus a community auth refresh token plugin and a built-in auth refresh token plugin. Each plugin is exposed as its own subpath in the package exports map.

Official sources

  1. Official documentation
  2. Official README
  3. Project repository
  4. Release notes
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/suhaotian-xior.svg)](https://hysenlabs.com/projects/suhaotian-xior)