Last active
December 11, 2018 22:04
-
-
Save piboistudios/f10d57df225effa72c28843e3951cf2d to your computer and use it in GitHub Desktop.
[ES6] This snippet of code prevents Axios from spamming a failing API endpoint
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const axios = require('axios').default.Axios; | |
const utils = require('axios/lib/utils'); | |
const buildURL = require('./buildURL'); | |
const endpointData = {} | |
const objectsMatch = (a, b) => { | |
const match = true; | |
for (var key in a) { | |
var aProp = a[key]; | |
var bProp = b[key]; | |
if (aProp !== bProp) { | |
match = false; | |
break; | |
} | |
} | |
return match; | |
} | |
const config = { | |
cooldownTime: 2500, | |
errorThreshhold: 10, | |
hitsPerSecond: 10, | |
startErrorWaitTime: 10, | |
backOffDegree: 1.25 | |
} | |
axios.prototype.getUri = function getUri(config) { | |
// config = mergeConfig(this.defaults, config); | |
return buildURL(config.url, config.params, config.paramsSerializer).replace(/^\?/, ''); | |
}; | |
axios.prototype.oldRequest = axios.prototype.request; | |
axios.prototype.request = function (axiosConfig) { | |
// console.log("Debounced request"); | |
const url = this.getUri(axiosConfig); | |
if (!endpointData[url]) endpointData[url] = { hitsPastSecond: 0, errors: 0, data: null, errorWait: config.startErrorWaitTime, canRequest: true }; | |
const urlData = endpointData[url]; | |
urlData.hitsPastSecond++; | |
setTimeout(() => { | |
urlData.hitsPastSecond-- | |
}, 1000); | |
// console.log({ endpointData, config, urlData, axiosConfig }); | |
return new Promise((resolve, reject) => { | |
const canRequest = urlData.hitsPastSecond <= config.hitsPerSecond && | |
(urlData.errors <= config.errorThreshhold || | |
!objectsMatch(axiosConfig.data, urlData.data)); | |
canRequest = canRequest && urlData.canRequest; | |
if (canRequest) { | |
urlData.data = config.data; | |
this.oldRequest(axiosConfig).then(resolve).catch(err => { | |
// console.log("Debouncing error", err); | |
urlData.errors++; | |
urlData.canRequest = false | |
setTimeout(() => { | |
urlData.canRequest = true; | |
}, urlData.errorWait); | |
urlData.errorWait = urlData.errorWait ** config.backOffDegree; | |
reject(err); | |
}); | |
} else { | |
setTimeout(() => resolve({ data: null }), config.cooldownTime) | |
} | |
}); | |
} | |
// Regenerate alias methods | |
utils.forEach(['delete', 'get', 'head', 'options'], function forEachMethodNoData(method) { | |
/*eslint func-names:0*/ | |
axios.prototype[method] = function (url, config) { | |
return this.request(utils.merge(config || {}, { | |
method: method, | |
url: url | |
})); | |
}; | |
}); | |
utils.forEach(['post', 'put', 'patch'], function forEachMethodWithData(method) { | |
/*eslint func-names:0*/ | |
axios.prototype[method] = function (url, data, config) { | |
return this.request(utils.merge(config || {}, { | |
method: method, | |
url: url, | |
data: data | |
})); | |
}; | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
'use strict'; | |
var utils = require('axios/lib/utils'); | |
function encode(val) { | |
return encodeURIComponent(val). | |
replace(/%40/gi, '@'). | |
replace(/%3A/gi, ':'). | |
replace(/%24/g, '$'). | |
replace(/%2C/gi, ','). | |
replace(/%20/g, '+'). | |
replace(/%5B/gi, '['). | |
replace(/%5D/gi, ']'); | |
} | |
/** | |
* Build a URL by appending params to the end | |
* | |
* @param {string} url The base of the url (e.g., http://www.google.com) | |
* @param {object} [params] The params to be appended | |
* @returns {string} The formatted url | |
*/ | |
module.exports = function buildURL(url, params, paramsSerializer) { | |
/*eslint no-param-reassign:0*/ | |
if (!params) { | |
return url; | |
} | |
var serializedParams; | |
if (paramsSerializer) { | |
serializedParams = paramsSerializer(params); | |
} else if (utils.isURLSearchParams(params)) { | |
serializedParams = params.toString(); | |
} else { | |
var parts = []; | |
utils.forEach(params, function serialize(val, key) { | |
if (val === null || typeof val === 'undefined') { | |
return; | |
} | |
if (utils.isArray(val)) { | |
key = key + '[]'; | |
} else { | |
val = [val]; | |
} | |
utils.forEach(val, function parseValue(v) { | |
if (utils.isDate(v)) { | |
v = v.toISOString(); | |
} else if (utils.isObject(v)) { | |
v = JSON.stringify(v); | |
} | |
parts.push(encode(key) + '=' + encode(v)); | |
}); | |
}); | |
serializedParams = parts.join('&'); | |
} | |
if (serializedParams) { | |
var hashmarkIndex = url.indexOf('#'); | |
if (hashmarkIndex !== -1) { | |
url = url.slice(0, hashmarkIndex); | |
} | |
url += (url.indexOf('?') === -1 ? '?' : '&') + serializedParams; | |
} | |
return url; | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This will prototype and 'rebuild' the original AxiosStatic object with debouncing logic injected into the request() method
Usage (in an ES6 environment):
import 'axios-debounced-failure.js'