api-mock-kit
v1.0.1
Published
Here’s a clean, production-ready README you can drop in. I’ve used **`fetch-mock-provider`** as the package name in examples—swap it if you pick a different name.
Readme
Here’s a clean, production-ready README you can drop in. I’ve used fetch-mock-provider as the package name in examples—swap it if you pick a different name.
fetch-mock-provider
A tiny, zero-dependency utility to mock fetch in browsers—either with a React <MockProvider> or a plain initMock() function.
Supports path params (/users/:id), regex matches, conditional mocking, transforming real API responses, and per-mock delays.
Ship it for dev/test. Hard-disable in prod.
Features
- 🧲 Intercepts
window.fetch(browser) - 🔎 URL matching via string patterns (with
:params) or RegExp - 🧩 Access to
urlParams,payload,pathVariables,method - 🔁 Transform mode: call the real API, then modify/shape its response
- 📦 Response mode: return mock data directly (object or function)
- ⏱ Per-mock delay (
truefor random,numberfor fixed) - 🚦 Global disable and log options
Install
npm i fetch-mock-provider
# or
yarn add fetch-mock-providerQuick Start
Option A — React Provider
import { MockProvider } from "fetch-mock-provider";
const mocks = [
{
url: "/users/:id",
method: "GET",
response: ({ pathVariables }) => ({
id: pathVariables.id,
name: "Mocked User",
}),
delay: true, // random 100–600ms
},
];
export default function App() {
return (
<MockProvider
mocks={mocks}
options={{
log: true,
disabled: process.env.NODE_ENV === "production",
}}
>
<YourApp />
</MockProvider>
);
}Option B — Plain JS/TS (no React)
import { initMock, resetMock } from "fetch-mock-provider";
initMock(
[
{
url: "/api/login",
method: "POST",
response: ({ payload }) => ({ token: "dev-token", echo: payload }),
delay: 300, // fixed 300ms
},
],
{
log: true,
disabled: process.env.NODE_ENV === "production",
}
);
// …later if you need to restore the original fetch:
resetMock();API
initMock(mocks: MockData[], options?: MockOptions): void
Patches window.fetch and begins intercepting calls.
resetMock(): void
Restores the original window.fetch.
<MockProvider mocks options>{children}</MockProvider>
React wrapper that calls initMock on mount and resetMock on unmount.
MockOptions
type MockOptions = {
disabled?: boolean; // disable all mocking (good for prod)
log?: boolean; // console.log matches and decisions
};MockData (discriminated union)
Base fields (apply to both modes):
type BaseMockConfig = {
url: string | RegExp; // string with :vars or RegExp (tested against pathname)
method: "GET" | "POST" | "PUT" | "DELETE" | "PATCH" | "HEAD" | "OPTIONS";
shouldMock?: (props: MockDataProps) => boolean;
delay?: number | boolean; // per-mock delay: true=random 100–600ms, number=fixed ms
};Response mode (return mock data directly):
type MockDataWithResponse = BaseMockConfig & {
response?: object | ((props: MockDataProps) => unknown);
};Transform mode (call real API, then modify its JSON):
type MockDataWithTransform = BaseMockConfig & {
transform: (props: MockDataProps & { actualResponse: unknown }) => unknown;
};Discriminated union:
type MockData = MockDataWithResponse | MockDataWithTransform;MockDataProps passed into response/transform/shouldMock
type MockDataProps = {
urlParams: Record<string, string>; // ?a=1&b=2 → { a:"1", b:"2" }
payload: unknown; // parsed JSON if possible, else string
pathVariables: Record<string, string>; // from ":vars" or regex named groups
method: "GET" | "POST" | "PUT" | "DELETE" | "PATCH" | "HEAD" | "OPTIONS";
};URL Matching Rules
String patterns: matched against the pathname only (domain ignored).
- Exact:
"/users/list" - With params:
"/users/:id"→{ pathVariables: { id: "42" } } - Segment count must match (no globbing).
- Exact:
RegExp: tested against the pathname only.
Named groups populate
pathVariables:url: /^\/orders\/(?<orderId>\d+)$/; // → pathVariables.orderId
Query string does not affect matching (it’s available in urlParams).
Both relative (/api/x) and absolute (https://api.com/x) URLs are supported; relative URLs are resolved against window.location.origin.
Examples
1) Conditional mocking with shouldMock
{
url: "/search",
method: "GET",
shouldMock: ({ urlParams }) => urlParams.mock === "1",
response: ({ urlParams }) => ({
results: [{ id: 1, q: urlParams.q ?? "" }],
mocked: true,
}),
}2) Regex + named capture groups
{
url: /^\/products\/(?<sku>[A-Z0-9\-]+)$/,
method: "GET",
response: ({ pathVariables }) => ({
sku: pathVariables.sku,
price: 999,
}),
delay: true,
}3) Transform real API responses
{
url: "/todos/:id",
method: "GET",
transform: ({ actualResponse }) => {
const todo = actualResponse as { id: number; title: string };
return { ...todo, title: todo.title.toUpperCase(), mocked: true };
},
}Troubleshooting
TypeError: Failed to construct 'URL': Invalid URLYou used a relative URL like/api/users. The library resolves these againstwindow.location.originautomatically. If you still see this, make sure your environment haswindow(browser/JSDOM).Payload isn’t JSON If the body isn’t JSON (e.g.,
FormData,Blob),payloadwill be a stringified fallback. If you need richer handling, add it in your app or extend the library.CORS still blocks Mocking doesn’t bypass CORS for non-mocked requests; only matched mocks are intercepted. For transform mode, the real request still hits the network and is subject to CORS.
Usage Notes
This targets browsers (uses
window.fetchandwindow.location). For Node, use a browser-like test env (e.g., JSDOM) or adapt the core.Don’t ship mocks to prod. Use:
{ disabled: process.env.NODE_ENV === "production"; }
Minimal Example (copy–paste)
import { initMock } from "fetch-mock-provider";
initMock(
[
{
url: "/users/:id",
method: "GET",
response: ({ pathVariables }) => ({ id: pathVariables.id, name: "Dev" }),
delay: true,
},
{
url: /^\/health$/,
method: "GET",
response: { ok: true },
},
{
url: "/todos/:id",
method: "GET",
transform: ({ actualResponse }) => {
const todo = actualResponse as { id: number; title: string };
return { ...todo, title: `[MOCKED] ${todo.title}` };
},
},
],
{ log: true, disabled: false }
);License
MIT
If you want, I can also generate a matching provider.tsx snippet that accepts options and a tight package.json + tsconfig so this README aligns 1:1 with your repo.
