Axios is a Promise-based HTTP client for browsers and Node.js, offering a simple API for handling HTTP requests. Analyzing its source code provides insights into its inner workings, enabling better usage and optimization. Below is a detailed breakdown of Axios’s source code, covering its core functionality and implementation.
Source Code Structure Overview
Axios’s source code structure is organized as follows:
axios/
│
├── lib/
│ ├── axios.js
│ ├── defaults.js
│ ├── utils.js
│ ├── core/
│ │ ├── Axios.js
│ │ ├── dispatchRequest.js
│ │ ├── InterceptorManager.js
│ │ ├── settle.js
│ │ ├── buildFullPath.js
│ │ ├── transformData.js
│ │ └── enhanceError.js
│ ├── cancel/
│ │ ├── Cancel.js
│ │ ├── CancelToken.js
│ │ └── isCancel.js
│ ├── adapters/
│ │ ├── xhr.js
│ │ └── http.js
│ ├── helpers/
│ │ ├── buildURL.js
│ │ ├── combineURLs.js
│ │ ├── isURLSameOrigin.js
│ │ ├── cookies.js
│ │ ├── parseHeaders.js
│ │ ├── spread.js
│ │ └── isAbsoluteURL.js
│ └── ...
├── dist/
├── examples/
├── test/
└── index.jsAxios Core Functionality
Creating Instances
Axios allows creating instances with custom configurations, useful for handling different API services.
const axios = require('axios');
const instance = axios.create({
baseURL: 'https://api.example.com',
timeout: 1000,
headers: {'X-Custom-Header': 'foobar'}
});
instance.get('/user')
.then(response => console.log(response))
.catch(error => console.error(error));Request Interceptors
Request interceptors enable modifying requests before they are sent.
const axios = require('axios');
axios.interceptors.request.use(
config => {
config.headers.Authorization = 'Bearer token';
return config;
},
error => Promise.reject(error)
);Response Interceptors
Response interceptors allow processing responses before they reach then or catch.
const axios = require('axios');
axios.interceptors.response.use(
response => response.data,
error => Promise.reject(error)
);Request Configuration
Axios provides various configuration options to customize requests.
const axios = require('axios');
axios({
method: 'post',
url: '/user/12345',
data: {
firstName: 'Fred',
lastName: 'Flintstone'
}
}).then(response => console.log(response));Request Methods
Axios offers shortcut methods for sending HTTP requests.
const axios = require('axios');
// GET request
axios.get('/user')
.then(response => console.log(response));
// POST request
axios.post('/user', {
firstName: 'Fred',
lastName: 'Flintstone'
}).then(response => console.log(response));
// Concurrent requests
axios.all([
axios.get('/user'),
axios.get('/posts')
]).then(axios.spread((userResponse, postsResponse) => {
console.log(userResponse);
console.log(postsResponse);
}));Axios Implementation Details
Axios Class
The Axios class is the core of Axios, encapsulating request and response handling logic.
lib/core/Axios.js:
function Axios(instanceConfig) {
this.defaults = instanceConfig;
this.interceptors = {
request: new InterceptorManager(),
response: new InterceptorManager()
};
}
Axios.prototype.request = function request(config) {
if (typeof config === 'string') {
config = arguments[1] || {};
config.url = arguments[0];
} else {
config = config || {};
}
config = mergeConfig(this.defaults, config);
config.method = config.method ? config.method.toLowerCase() : 'get';
const chain = [dispatchRequest, undefined];
let promise = Promise.resolve(config);
this.interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) {
chain.unshift(interceptor.fulfilled, interceptor.rejected);
});
this.interceptors.response.forEach(function pushResponseInterceptors(interceptor) {
chain.push(interceptor.fulfilled, interceptor.rejected);
});
while (chain.length) {
promise = promise.then(chain.shift(), chain.shift());
}
return promise;
};
// Shortcut methods for HTTP methods
['delete', 'get', 'head', 'options'].forEach(function forEachMethodNoData(method) {
Axios.prototype[method] = function(url, config) {
return this.request(mergeConfig(config || {}, {
method: method,
url: url
}));
};
});
['post', 'put', 'patch'].forEach(function forEachMethodWithData(method) {
Axios.prototype[method] = function(url, data, config) {
return this.request(mergeConfig(config || {}, {
method: method,
url: url,
data: data
}));
};
});InterceptorManager Class
The InterceptorManager class manages request and response interceptors.
lib/core/InterceptorManager.js:
function InterceptorManager() {
this.handlers = [];
}
InterceptorManager.prototype.use = function use(fulfilled, rejected) {
this.handlers.push({
fulfilled: fulfilled,
rejected: rejected
});
return this.handlers.length - 1;
};
InterceptorManager.prototype.eject = function eject(id) {
if (this.handlers[id]) {
this.handlers[id] = null;
}
};
InterceptorManager.prototype.forEach = function forEach(fn) {
this.handlers.forEach(function forEachHandler(h) {
if (h !== null) {
fn(h);
}
});
};Request Configuration
Request configurations combine user-provided settings with defaults.
lib/core/mergeConfig.js:
function mergeConfig(config1, config2) {
config2 = config2 || {};
const config = {};
const mergeDeepProperties = ['headers', 'auth', 'proxy', 'params'];
const valueFromConfig2Keys = ['url', 'method', 'params', 'data'];
const defaultToConfig2Keys = ['baseURL', 'timeout', 'timeoutErrorMessage', 'withCredentials'];
mergeDeepProperties.forEach(function deepMerge(prop) {
if (typeof config2[prop] !== 'undefined') {
config[prop] = utils.deepMerge(config1[prop], config2[prop]);
} else if (typeof config1[prop] !== 'undefined') {
config[prop] = utils.deepMerge(config1[prop]);
}
});
valueFromConfig2Keys.forEach(function valueFromConfig2(prop) {
if (typeof config2[prop] !== 'undefined') {
config[prop] = config2[prop];
}
});
defaultToConfig2Keys.forEach(function defaultToConfig2(prop) {
if (typeof config2[prop] !== 'undefined') {
config[prop] = config2[prop];
} else if (typeof config1[prop] !== 'undefined') {
config[prop] = config1[prop];
}
});
const axiosKeys = mergeDeepProperties.concat(valueFromConfig2Keys).concat(defaultToConfig2Keys);
const otherKeys = Object.keys(config2).concat(Object.keys(config1)).filter(function filterAxiosKeys(key) {
return axiosKeys.indexOf(key) === -1;
});
otherKeys.forEach(function otherConfig(prop) {
if (typeof config2[prop] !== 'undefined') {
config[prop] = config2[prop];
} else if (typeof config1[prop] !== 'undefined') {
config[prop] = config1[prop];
}
});
return config;
}Request Dispatching
The dispatchRequest function handles request sending.
lib/core/dispatchRequest.js:
function dispatchRequest(config) {
throwIfCancellationRequested(config);
config.headers = config.headers || {};
config.data = transformData(
config.data,
config.headers,
config.transformRequest
);
config.headers = utils.merge(
config.headers.common || {},
config.headers[config.method] || {},
config.headers || {}
);
['delete', 'get', 'head', 'post', 'put', 'patch', 'common'].forEach(function cleanHeaderConfig(method) {
delete config.headers[method];
});
const adapter = config.adapter || defaults.adapter;
return adapter(config).then(function onAdapterResolution(response) {
throwIfCancellationRequested(config);
response.data = transformData(
response.data,
response.headers,
config.transformResponse
);
return response;
}, function onAdapterRejection(reason) {
if (!isCancel(reason)) {
throwIfCancellationRequested(config);
if (reason && reason.response) {
reason.response.data = transformData(
reason.response.data,
reason.response.headers,
config.transformResponse
);
}
}
return Promise.reject(reason);
});
}Request and Response Interceptors
Interceptors are executed before requests are sent and before responses are processed.
lib/core/Axios.js:
Axios.prototype.request = function request(config) {
if (typeof config === 'string') {
config = arguments[1] || {};
config.url = arguments[0];
} else {
config = config || {};
}
config = mergeConfig(this.defaults, config);
config.method = config.method ? config.method.toLowerCase() : 'get';
const chain = [dispatchRequest, undefined];
let promise = Promise.resolve(config);
this.interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) {
chain.unshift(interceptor.fulfilled, interceptor.rejected);
});
this.interceptors.response.forEach(function pushResponseInterceptors(interceptor) {
chain.push(interceptor.fulfilled, interceptor.rejected);
});
while (chain.length) {
promise = promise.then(chain.shift(), chain.shift());
}
return promise;
};Axios Advanced Features
Canceling Requests
Axios supports request cancellation via CancelToken.
lib/cancel/CancelToken.js:
function CancelToken(executor) {
if (typeof executor !== 'function') {
throw new TypeError('executor must be a function.');
}
let resolvePromise;
this.promise = new Promise(function promiseExecutor(resolve) {
resolvePromise = resolve;
});
const token = this;
executor(function cancel(message) {
if (token.reason) {
return;
}
token.reason = new Cancel(message);
resolvePromise(token.reason);
});
}
CancelToken.prototype.throwIfRequested = function throwIfRequested() {
if (this.reason) {
throw this.reason;
}
};Using CancelToken to Cancel Requests:
const CancelToken = axios.CancelToken;
let cancel;
axios.get('/user/12345', {
cancelToken: new CancelToken(function executor(c) {
cancel = c;
})
});
// Cancel the request
cancel();Concurrent Requests
Axios supports concurrent requests using axios.all and axios.spread.
axios.all([
axios.get('/user'),
axios.get('/posts')
]).then(axios.spread((userResponse, postsResponse) => {
console.log(userResponse);
console.log(postsResponse);
}));Custom Instances
Create custom instances with axios.create for tailored configurations.
const instance = axios.create({
baseURL: 'https://api.example.com',
timeout: 1000,
headers: {'X-Custom-Header': 'foobar'}
});
instance.get('/user')
.then(response => console.log(response))
.catch(error => console.error(error));Error Handling
Axios provides robust error handling for various stages, including requests, responses, and cancellations.
axios.get('/user/12345')
.then(response => console.log(response))
.catch(error => {
if (error.response) {
// Server responded with a status outside 2xx
console.log(error.response.data);
console.log(error.response.status);
console.log(error.response.headers);
} else if (error.request) {
// Request made but no response received
console.log(error.request);
} else {
// Error during request setup
console.log('Error', error.message);
}
});Summary
Understanding Axios’s source code reveals its operational principles, empowering developers to leverage its capabilities fully. By exploring its core functionality, implementation details, and advanced features, we can write more efficient and robust HTTP request code.



