mrkeyoor.com_
Wed 05 Aug 05:04 UTC
npmWeb Backendupdated 05 Aug 2026

axios

Promise-based HTTP client that runs in both the browser (via XMLHttpRequest) and Node.js (via the http module). One API for all request methods with automatic JSON serialization, request and response interceptors, upload and download progress events, cancellation via AbortController, and built-in encoding for multipart/form-data and urlencoded bodies. It sat in nearly every JavaScript project before fetch went universal, and its interceptor model is still the main reason teams keep reaching for it.

Verdict

Still the safe boring choice when you need interceptors or wide runtime coverage. For new projects on Node 18+ making plain JSON calls, native fetch or ky does the same job without the dependency.

API stability5/5v1.x has been stable since 2022; the config object and interceptor API are unchanged for years, and deprecated pieces like CancelToken keep working
Docs4/5One long, thorough README plus axios-http.com covering every option with examples; finding a specific edge case means scrolling a very large single page
Maintenance4/5Active releases (1.19.0 shipped July 2026), pushes within the last day, and only around 60 open issues and PRs on a repo of this size; still largely volunteer-driven
Ecosystem5/5118M weekly downloads, 109k stars, and a big plugin scene (axios-retry, axios-mock-adapter); nearly every JS API wrapper documents an axios example

Use it if

  • You want request/response interceptors for auth tokens, logging, or error normalization without wrapping fetch yourself
  • You need one HTTP client that behaves identically in browsers and Node, including Node versions without a stable native fetch
  • You want automatic JSON parsing, query param serialization, and multipart/form-data encoding out of the box
  • You need upload or download progress events, which plain fetch still makes awkward
Skip it if

Setup reality

npm install axios and it works: no peer dependencies, no config, TypeScript types included. The real work starts when you productionize. Nothing retries by default, timeout defaults to 0 (wait forever), and response size limits default to unlimited, so a serious setup means an axios.create() instance with baseURL, timeout, maxContentLength, and interceptors. Two current gotchas: CancelToken is deprecated in favor of AbortController, and some strict backends reject axios's readable query encoding until you override paramsSerializer.encode with encodeURIComponent.

Patterns

Fetch JSON with error-aware handlingget-json

import axios from 'axios';

const { data, status } = await axios.get('https://api.example.com/users/1');
console.log(status, data);

axios parses JSON automatically and rejects on non-2xx status, unlike fetch which resolves on 404s.

POST a JSON bodypost-json

const res = await axios.post('/api/users', {
  name: 'Ada',
  email: 'ada@example.com',
});
console.log(res.data.id);

Plain objects are serialized to JSON with the right Content-Type header; no JSON.stringify needed.

Create a configured instancecreate-instance

const api = axios.create({
  baseURL: 'https://api.example.com/v1',
  timeout: 10000,
  maxContentLength: 10 * 1024 * 1024,
  headers: { 'X-Client': 'my-app' },
});

const { data } = await api.get('/orders');

timeout defaults to 0 (never) and maxContentLength to unlimited; set both on every production instance.

Attach an auth token with a request interceptorauth-interceptor

api.interceptors.request.use((config) => {
  config.headers.Authorization = `Bearer ${getToken()}`;
  return config;
});

Request interceptors run last-registered-first; response interceptors run in registration order.

Distinguish HTTP errors from network errorshandle-errors

try {
  await api.get('/thing');
} catch (err) {
  if (axios.isAxiosError(err)) {
    if (err.response) {
      console.error('HTTP', err.response.status, err.response.data);
    } else if (err.request) {
      console.error('No response (network/timeout)', err.code);
    }
  } else {
    throw err;
  }
}

err.response exists only when the server answered; timeouts surface as code ECONNABORTED (or ETIMEDOUT with clarifyTimeoutError).

Cancel a request with AbortControllertimeout-cancel

const controller = new AbortController();

const promise = axios.get('/slow', {
  signal: controller.signal,
  timeout: 5000,
});

// somewhere else
controller.abort();

Use AbortController; the older CancelToken API is deprecated since v0.22 and should not appear in new code.

Retry a failed request with an interceptorhttp-retry

api.interceptors.response.use(undefined, async (error) => {
  const cfg = error.config;
  cfg.__retries = (cfg.__retries || 0) + 1;
  const retryable = !error.response || error.response.status >= 500;
  if (retryable && cfg.__retries <= 3) {
    await new Promise((r) => setTimeout(r, 300 * 2 ** cfg.__retries));
    return api.request(cfg);
  }
  return Promise.reject(error);
});

axios has no built-in retry; for anything beyond this, use the axios-retry package instead of growing this interceptor.

Send query parametersquery-params

await axios.get('/search', {
  params: { q: 'printers', page: 2 },
  // strict RFC 3986 encoding for picky backends:
  paramsSerializer: { encode: encodeURIComponent },
});

By default axios keeps characters like : and $ readable in query strings; some backends require the strict encoding shown here.

Upload a file as multipart/form-dataupload-formdata

const form = new FormData();
form.append('file', fileInput.files[0]);
form.append('caption', 'invoice scan');

await axios.post('/upload', form, {
  onUploadProgress: (e) => {
    if (e.total) console.log(Math.round((e.loaded / e.total) * 100) + '%');
  },
});

Do not set the Content-Type header yourself; axios sets the multipart boundary automatically.

Track download progressdownload-progress

const res = await axios.get('/big-file.zip', {
  responseType: 'blob',
  onDownloadProgress: (e) => {
    if (e.total) console.log(e.loaded, 'of', e.total);
  },
});

e.total is undefined when the server omits Content-Length; guard before computing percentages.

Cap response size against decompression bombslimit-response-size

axios.defaults.maxContentLength = 10 * 1024 * 1024; // 10 MB
axios.defaults.maxBodyLength = 10 * 1024 * 1024;

Both default to -1 (unlimited); a hostile server can return a tiny gzip body that expands to gigabytes in your Node process.

Alternatives

PackageRegistryPick it when
kynpmBrowser-first fetch wrapper at a fraction of the size, with retries built in
gotnpmNode-only projects that want retries, hooks, and streams without plugins
node-fetchnpmYou want the plain fetch API shape on Node versions lacking stable native fetch