banjocode Intercepting a Fetch Request

Intercepting a Fetch Request

A stumbled upon a nice script that helped a lot of people to book a train in Sweden, and got interested in how it works. This is just a quick summary of that.

4 min read

Intercepting a fetch request

Every christmas, people in Sweden want to go home. And once the train tickets gets released, the servers crash. Every year. This neat script was published in several news papers. It modifies the fetch request to run several times until it succeed with an exponential back-off delay, not to overwork their servers.

window._fetch = window.fetch;
window.fetch = (...args) => {
  return new Promise((resolve, reject) => {
    const maxRetries = 10
    let retries = 0
    const retry = () => {
      window._fetch(...args)
        .then((data) => {
          if (data.status === 429 && retries < maxRetries) {
            retries++
            setTimeout(retry, 500 * Math.pow(2, retries))
          } else {
            resolve(data);
          }
        }).catch(reject)
    }
    retry()
  })
};

If it receives a specific answers, it reruns the script until it either succeeds or have tried 10 times.

Intercepting in general

I though this was a very nice approach, and started to read more about it. The ability to actually intercept a core feature/API of the browser and modify it opens up for some very interesting possibilities. I found some nice tips and tricks on this SO question, if you want to intercept the Fetch API yourself. You can read up upon it yourself, but this is another (similar) approach to intercept and modify.

const {fetch: origFetch} = window;
window.fetch = async (...args) => {
  console.log("fetch called with args:", args);
  const response = await origFetch(...args);
  
  /* work with the cloned response in a separate promise
     chain -- could use the same chain with `await`. */
  response
    .clone()
    .json()
    .then(body => console.log("intercepted response:", body))
    .catch(err => console.error(err))
  ;
    
  /* the original response can be resolved unmodified: */
  //return response;
  
  /* or mock the response: */
  return {
    ok: true,
    status: 200,
    json: async () => ({
      userId: 1,
      id: 1,
      title: "Mocked!!",
      completed: false
    })
  };
};