A JavaScript API retry pattern
Taking an object literal used as a wrapper around an API service.
const service = {
find: async (filter) => {/*...*/},
getEntry: async (id) => {/*...*/},
updateEntry: async (id, update) => {/*...*/},
}
With an API service that returns a limit response such as Promise.reject(new Error('Quota limit exceeded'))
.
One approach for a retry mechanism is to use a Proxy
handler for each method.
const serviceWithProxy = { ...service };
let count = 0;
const handler = {
apply: async (target, thisArg, argumentsList) => {
try {
return await service[target.name](...argumentsList);
} catch (e) {
if (e.message === "Quota limit exceeded" && count < 50) {
// random wait time between 300ms - 2300ms
await new Promise((resolve) =>
setTimeout(resolve, Math.floor(Math.random() * 2000 + 300))
);
count++;
return await serviceWithProxy[target.name](...argumentsList);
}
throw new Error(e);
}
},
};
And to apply the handler.
Object.keys(serviceWithProxy).forEach((key) => {
if (typeof serviceWithProxy[key] === "function") {
serviceWithProxy[key] = new Proxy(serviceWithProxy[key], handler);
}
});
module.exports = { default: serviceWithProxy, serviceWithoutProxy: service };
The count
variable could be used as part of the wait time calculation to switch to an exponential backoff implementation.
Read other posts