I've been working in the web development field for more than 5years now, but one question has been in my mind since day one: Why Axios is always the "Go to" for HTTP client? Why there seem aren't any competitors?
Although there are a lot of articles about the fetch API, I still found that having an interceptor(middleware pattern) and a configurable instance of the client is essential, especially for commercial-grade applications that have multiple HTTP clients, require adding a lot of headers here and there and complex error handling.
Motivation
1. Axios has had a lot of breaking changes recently
Recently Axios shipped out v0.27.0 which includes some breaking changes and bugs. It's hard to imagine an over 8 years package still not v1.0.0. Although they finally planned to follow semantic versioning (ref: github.com/axios/axios/issues/4614). It already brings a lot of trouble for auto package management like Renovate. Not to mention its fragile codebase.
2. Axios codebase is fragile
The trigger point of this move is that v0.27.0 breaks FormData handling unintentionally(ref: https://github.com/axios/axios/pull/4640). Solving a mobile problem brings in a browser bug. This shows the current maintainer(s) lack the knowledge of the platform it's supporting and how its ancient code works. Not to metion v0.27.0 is shipped 1 year after the the PR merged, more issue just left there for a few years unresolved. With it's low momentum, I doubt this package will move forward steady.
// Some example for weird design choice, it calls your interceptor in the reverse order.
// ref: https://github.com/axios/axios/issues/841
const instance = axios.create();
instance.interceptors.request.use(function () {/*This will run second*/});
instance.interceptors.request.use(function () {/*This will run first*/});
3. Axios is still using the old XMLHttpRequests API
Part of the reason for this is Axios is still using the old XMLHttpRequests API. It has to manually implement a lot of handling and hacks we get for free with Fetch API. Fetch API should be the standard in 2022 as most browser supports it. Fetch API based HTTP client also result in smaller bundle size in general.
So what are the choices out there?
So the objective is pretty clear, a Fetch API based HTTP client with good looking syntax and active maintenance. After a bit of research, there are a few choices, but not all fit my criteria:
- Got(Node Js only)
- superagent(also XMLHttpRequests based)
- Gaxios(Node Js only)
Finally, I got 2 solid contestants, Ky and Wretch. Both are new Fetch API based HTTP clients with small bundle sizes (~3KB, Axios is 6.7KB)
Ky
Ky is created by the same group of Got, which offer some interesting feature. Like treats non-2xx status codes as errors, retries failed requests and event hooks(like Axios interceptor).
const instance = ky.create({
headers: {
rainbow: 'rainbow',
unicorn: 'unicorn'
}
});
instance.extend({
hooks: {
beforeRequest: [
async ({request, options, error, retryCount}) => {/*request interceptor*/}
]
}
});
instance.extend({
hooks: {
beforeError: [
async (error) => {/*error interceptor*/}
]
}
});
instance.extend({
hooks: {
afterResponse: [
async (error) => {/*response interceptor*/}
]
}
});
Wretch
Wretch on the other hand takes the function chaining approach. It split common error types into separate helper methods so you don't need to result in an interceptor every time
const instance = wretch()
.auth("Basic d3JldGNoOnJvY2tz")
.headers({ "Content-Type": "text/plain", Accept: "application/json" })
// helper methods to directly intercept common error
instance()
.badRequest(err => console.log(err.status))
.unauthorized(err => console.log(err.status))
.forbidden(err => console.log(err.status))
.notFound(err => console.log(err.status))
.timeout(err => console.log(err.status))
.internalError(err => console.log(err.status))
.error(418, err => console.log(err.status))
.fetchError(err => console.log(err))
.res()
instance.middlewares([
next => (url, opts) => {/*request interceptor*/}
])
Why Wretch not Ky
When you go NPM trends you may find Ky has more downloads stars etc... than Wretch, but why do I still go for Wretch?
Although Ky has more stars and downloads, I think it's mostly driven by the hype of the creator of Got. After 6 years of its creation, it also hasn't passed v1.0.0. The creator already creates a milestone for v1.0.0 but it's more than half a year ago with little progress. Wretch on the other hand is already v1.7.9 with planning for v2. So as an adaptor right now, Ky is riskier than Wretch. On top of that, I'm more into the function chaining approach than the object config in Ky, were registering a middlewares with a function is cleaner than extends()
in Ky. But it is kind of subjective, I will leave you guys be the judge.
Top comments (9)
I like the availability of
fetch
in the browser and NodeJS. HopefullyWretch
works on both since it is afetch
wrapper.I uses Wretch dail for both BE and FE, it works perfectly with the node 18 native fetch without any polyfill
I always use Axios or Fetch might give Wretch a try.
I've been an axios stan for some time but I would look into Ky and Wretch. Great article!
Axios uses XMLHTTPRequest because it support download progress
you can also do it via fetch
the XMLHTTPRequest onprogress event is using the
Content-Length
header to calc the progressyou can implement the same logic using fetch
ref: stackoverflow.com/questions/472851...
I recently found ProgressAddon in wretch so you don't have to implement it on your own
How do I create custom interceptor but for response?
instance.middlewares([
next => (url, opts) => {/*request interceptor*/}
])