Skip to content

Instantly share code, notes, and snippets.

@ErrorxCode
Last active July 2, 2025 19:17
Show Gist options
  • Save ErrorxCode/22bec804e3b1ca426636440dcc840d84 to your computer and use it in GitHub Desktop.
Save ErrorxCode/22bec804e3b1ca426636440dcc840d84 to your computer and use it in GitHub Desktop.
Proxying and modifying response of network API calls made using fetch API or XMLHttpRequest API using external javascript
function createMatcher(pattern) {
// escape regex chars, then replace * with .*
const esc = pattern.replace(/[.+?^${}()|[\]\\]/g, '\\$&');
const regex = new RegExp('^' + esc.replace(/\*/g, '.*') + '$');
return url => regex.test(url);
}
// ------------------------------
// Core Interceptor Object
// ------------------------------
const Interceptor = {
fetch: {
_mocks: [],
_enabled: false,
_origFetch: window.fetch,
mock(pattern, handler) {
this._mocks.push({ matcher: createMatcher(pattern), handler });
if (!this._enabled) this._enable();
},
_enable() {
this._enabled = true;
const self = this;
window.fetch = async function (input, init) {
const url = typeof input === 'string' ? input : input.url;
const entry = self._mocks.find(m => m.matcher(url));
if (!entry) {
return self._origFetch.apply(this, arguments);
}
// Build a Request-like object
const req = new Request(input, init);
const res = await self._origFetch.apply(this, arguments);
const clone = res.clone();
// Let user handler mutate or replace
const ret = await entry.handler(req, clone);
if (ret instanceof Response) {
return ret;
}
// Wrap return value into a Response
const body = typeof ret === 'string' ? ret : JSON.stringify(ret);
return new Response(body, {
status: res.status,
statusText: res.statusText,
headers: res.headers
});
};
},
restore() {
if (this._enabled) {
window.fetch = this._origFetch;
this._mocks.length = 0;
this._enabled = false;
}
}
},
xhr: {
_mocks: [],
_enabled: false,
_origSend: XMLHttpRequest.prototype.send,
_origOpen: XMLHttpRequest.prototype.open,
mock(pattern, handler) {
this._mocks.push({ matcher: createMatcher(pattern), handler });
if (!this._enabled) this._enable();
},
_enable() {
this._enabled = true;
const self = this;
// 1) Intercept open to grab method+url
XMLHttpRequest.prototype.open = function (method, url, ...rest) {
this._mockInfo = { method, url, body: null };
return self._origOpen.apply(this, arguments);
};
// 2) Intercept send to grab body + attach readyState handler
XMLHttpRequest.prototype.send = function (body) {
if (this._mockInfo) {
this._mockInfo.body = body;
this.addEventListener('readystatechange', async () => {
if (this.readyState === 4) {
const url = this.responseURL;
const entry = self._mocks.find(m => m.matcher(url));
if (!entry) return;
// Build req/res facades
const req = {
url: this._mockInfo.url,
method: this._mockInfo.method,
body: this._mockInfo.body
};
const res = {
text: () => this.responseText,
json: () => {
try { return JSON.parse(this.responseText); }
catch { return null; }
}
};
// Invoke handler
const ret = await entry.handler(req, res);
// Determine new responseText
const newBody =
typeof ret === 'string'
? ret
: JSON.stringify(ret);
// Override responseText & response
Object.defineProperty(this, 'responseText', {
get: () => newBody, configurable: true
});
Object.defineProperty(this, 'response', {
get: () => newBody, configurable: true
});
}
});
}
return self._origSend.apply(this, arguments);
};
},
restore() {
if (this._enabled) {
XMLHttpRequest.prototype.send = this._origSend;
XMLHttpRequest.prototype.open = this._origOpen;
this._mocks.length = 0;
this._enabled = false;
}
}
}
};

🧩 Interceptor API Mocking Guide

A lightweight utility for intercepting and mocking fetch and XMLHttpRequest responses using wildcard URL matching.

Implementation/Intsallation

Just load this script into the webpage on which you want to mock the API response. You can inject the script into the dom with the javascript like this

const script = document.createElement('script');
script.src = 'interceptor.js';
script.onload = () => console.log('Interceptor loaded');
document.head.appendChild(script);

NOTE : If you are using this in a browser extension, this code must be executed in your webpage context (Content script running in MAIN world) otherwise it won't work

πŸ“¦ Example Usage

Interceptor.fetch.mock('https://*.example.com/api/*', async (req, res) => {
  const data = await res.json();
  data.injected = true;
  return new Response(JSON.stringify(data), res);
});

Interceptor.xhr.mock('https://xyz.example.com/api/*', async (req, res) => {
  const obj = await res.json();
  obj.hijacked = true;
  return obj;  // will be JSON-stringified under the hood
});

// Later, if you need to undo all mocking:
Interceptor.fetch.restore();
Interceptor.xhr.restore();

πŸ”— Wildcard Matching

Supports * to match URLs, e.g.:

  • 'https://api.example.com/*'
  • 'https://*.example.com/data/*'

πŸ” Request/Response Objects

req (both fetch & xhr)

  • .url – full URL
  • .method – HTTP method (GET, POST, etc.)
  • .body – request body (for XHR only)

res (facade)

  • .json() – parses original JSON body
  • .text() – returns original response as string

For fetch, this is the actual Response object. For xhr, it's a wrapper.

🎯 Behavior Details

Feature Behavior
Methods Supported All (GET, POST, PATCH, etc.)
Wildcard * matching βœ… Yes (pattern β†’ RegExp)
Response override type Response, JSON object, or string
.restore() supported βœ… Restores native behavior

⚠️ Exceptional Cases

🧊 Error or Non-OK Responses

All status codes are intercepted unless filtered:

if (!res.ok) return res; // Optional: skip on 404/500 etc.

πŸ“‘ HEAD Requests

These return no body by design. Ensure your handler doesn’t assume one.

🧠 Binary Data (XHR only)

Extend the facade if you need res.blob() or res.arrayBuffer() for XHR.

That’s your streamlined, mock-it-anywhere kit. Let me know if you want a bundlable version or automatic test snapshot hooks!

Support

This gist comprise of lot's of energy, brainstroming, coding, testing and time. If this project has helped you in any way, please consider giving it a ⭐ to show your support and help it grow!

Readme Quotes

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment