Mock API Server
Frontend and backend development often need to happen in parallel. The Mock API Server lets you define fake endpoints with custom responses so your frontend code has something to work against before the real API exists.
Mock API Server
Fake REST endpoints — no Node.js, no backend needed.
What is a mock API and why do developers use one?
A mock API is a simulated HTTP server that returns pre-defined responses to HTTP requests without executing real business logic or touching a database. Instead of pointing your frontend at a live backend, you point it at the mock, which replies with whatever JSON you configure. From the perspective of your JavaScript code, the response is indistinguishable from a real one — it arrives over the same fetch call, carries the same HTTP status code, and contains the same JSON structure.
The most common reason to use a mock API is parallel development. In a typical product team, the backend engineers and frontend engineers agree on a contract — the shape of the request and the shape of the response — and then split off to build their respective sides simultaneously. Without a mock, the frontend team is blocked until the backend endpoints are ready and deployed to a shared environment. A mock API eliminates that dependency entirely. Frontend engineers can build, wire up, and iterate on their UI without waiting for a single line of backend code to exist.
The second major use case is testing edge casesthat are difficult or impossible to reproduce against a real backend. What happens in your UI when the server returns a 500 error? What does your loading skeleton look like when the API takes three seconds to respond? What does the empty-state component render when the API returns an empty array? With a real backend you'd need to engineer special conditions to trigger each of these scenarios. With a mock, you define them declaratively and switch between them instantly.
A third use case is demos and prototypes. If you want to show a stakeholder or client how a feature will look before any backend infrastructure exists, a mock API lets you wire up a fully interactive prototype in hours rather than days. The demo experience is indistinguishable from a real integration.
Finally, mock APIs are valuable for reliable test data. Real APIs are stateful — running a test suite against a live database can leave behind side effects that cause subsequent runs to fail. A mock always returns exactly what you configured, making your test suite deterministic and reproducible regardless of the order in which tests run.
How browser-based mocking works
Traditional mock servers require running a Node process (json-server, Express, etc.) on your machine. This tool uses a Service Worker to intercept fetch requests from the same browser tab. When your code calls fetch('/api/users'), the Service Worker matches it against your configured routes and returns the response — no actual network request happens.
Service Workers are a browser-native technology that sit between your web page and the network. Originally designed for offline Progressive Web Apps, they can intercept any outgoing fetch request and respond to it programmatically. This means the mock runs entirely inside your browser — there is no server process, no port to configure, and no firewall rules to worry about. The Service Worker code is sandboxed to its origin and cannot affect other tabs or other websites.
When a route does not match any configured mock, the request is passed through to the real network. This is important: it means you can mock only the endpoints you care about and let everything else — your CDN assets, analytics, third-party scripts — continue to work normally.
Request flow
Defining mock routes
Each mock route has five fields: method, path, status code, response body (JSON), and optional delay. You can add as many routes as you need.
The method field accepts any standard HTTP verb — GET, POST, PUT, PATCH, DELETE, HEAD, or OPTIONS. The path supports static segments such as /api/users and parameterised segments such as /api/users/:id. When using a parameterised segment, the mock will match any request that fits the pattern regardless of what the actual :id value is — useful for simulating both a successful look-up and a not-found case on the same endpoint type.
The response body is a JSON editor. You type (or paste) the exact JSON object or array you want the mock to return. The editor validates the JSON on every keystroke and highlights syntax errors before you save. Here is an example response body for a user list endpoint:
{
"data": [
{ "id": 1, "name": "Alice Chen", "role": "admin", "active": true },
{ "id": 2, "name": "Bob Patel", "role": "editor", "active": true },
{ "id": 3, "name": "Carol Kim", "role": "viewer", "active": false }
],
"meta": {
"total": 3,
"page": 1,
"per_page": 20
}
}Notice the meta wrapper — it reflects the envelope structure many real APIs use for paginated responses. By matching your mock data to the shape your real API will eventually return, you ensure that all the frontend code you write against the mock will work without changes once the real backend is ready.
Example route configuration
| Method | Path | Status | Delay | Purpose |
|---|---|---|---|---|
| GET | /api/users | 200 | 0ms | Return user list |
| POST | /api/login | 200 | 300ms | Simulate auth with realistic delay |
| GET | /api/users/1 | 404 | 0ms | Test 404 handling |
| DELETE | /api/items/:id | 500 | 100ms | Test server error UI |
Simulating status codes
One of the most powerful aspects of a mock API is the ability to return any HTTP status code on demand. Real backends rarely produce certain error codes in a way that's easy to trigger repeatedly during development — a 503 Service Unavailable, for example, would require the real server to be degraded. With a mock you can pin a route to any status code and test your UI's response in seconds.
The commonly useful status codes and what to test with each:
Standard success. Use for the happy path.
POST endpoints that create a resource. Tests that your UI shows a success confirmation.
Invalid input. Tests that your form validation error messages render correctly.
Missing or invalid credentials. Tests that your app redirects to the login page.
Authenticated but lacking permission. Tests role-based access control in the UI.
Resource does not exist. Tests your not-found component or empty state.
Server-side validation failed. Return field-level error messages in the body.
Unexpected server crash. Tests your generic error boundary or retry mechanism.
A particularly useful pattern is defining two routes with the same method and path — one returning 200 and one returning 500 — and toggling between them. This lets you quickly flip a route between the happy path and the error state without re-editing the JSON body each time.
Simulating real-world conditions: latency and delays
A mock that responds instantly every time is not realistic. Real networks have variable latency — a mobile user on a congested 4G network might experience 400-800ms of round-trip time for a simple API call. If you develop entirely against instant responses, you may ship a UI that has no loading states, freezes during slow fetches, or shows jarring content jumps when data loads too quickly for the eye to track.
The delay field on each route solves this. Set it to any value in milliseconds and the Service Worker will wait that long before returning the response. Common useful delay values:
0 msInstant — good for unit test scenarios where you care only about data, not timing.
100–300 msFast broadband or LAN. Tests that your loading skeleton appears but disappears quickly.
400–800 msTypical 4G mobile. The most realistic delay for consumer-facing products.
1000–2000 msSlow 3G or high server load. Stresses your timeout UX and progress indicators.
3000+ msNear-timeout territory. Useful for testing cancellation, retry buttons, and timeout error messages.
Delays are set per-route, so you can mix fast and slow responses in the same session — for example, a fast user profile load combined with a slow analytics summary that simulates an expensive aggregation query. This allows you to build and test skeleton loaders that appear independently as each piece of data arrives.
Response delay
Add 100-2000ms delays to simulate network latency and test loading states in your UI.
Error status codes
Return 400, 401, 403, 404, 422, 500, 503 to test every error handling path in your frontend.
JSON templates
Use preset JSON templates for common patterns (user objects, paginated lists, error envelopes) to save time.
Typical development workflows
Understanding when to bring in a mock API — and how to structure your work around it — makes a bigger difference than any individual feature. Here are the patterns that work best in practice.
Contract-first parallel development
At the start of a feature, the frontend and backend engineers meet for fifteen minutes to agree on the API contract: the HTTP method, the URL path, the request body shape, and the response body shape. This agreement is written down — ideally as an OpenAPI snippet or even just a JSON example in a shared doc. The frontend engineer then creates a mock route that matches the agreed contract and starts building the UI immediately. The backend engineer writes the real implementation. When the backend lands, swapping from mock to real requires changing one base URL — nothing else.
Systematic error state coverage
Before shipping a feature, go through every API call it makes and define a mock variant for each error code the real API can return. Flip to each variant in turn and manually verify that the UI handles it gracefully: does the 401 route to the login page? Does the 422 surface the right field-level error? Does the 500 show a user-friendly message rather than a blank screen? This systematic sweep is far more efficient than trying to reproduce these conditions in a real environment.
Demo and stakeholder review
When preparing a demo, configure mocks that return polished, realistic-looking data — proper names, sensible numbers, plausible dates. Disable any routes that would produce loading spinners or empty states unless the demo specifically covers those. Because the mock is defined per-tab, you can have a "demo mode" set of routes open in one window and your development configuration in another.
Automated testing with deterministic data
When writing end-to-end tests with tools like Playwright or Cypress, you want the same data every time so assertions do not flake. Export your mock route configuration as JSON and commit it to your repository. In CI, the test runner loads the mock configuration before each test run, guaranteeing that every fetch call returns exactly the expected data regardless of the state of the real backend.
Building realistic mock responses
The quality of your mock data directly affects how useful the mock is. Minimal placeholder data — single-character strings, sequential IDs, all-zero timestamps — makes it harder to spot layout bugs in your UI and gives an unrealistic picture during demos. Invest a few extra minutes in crafting realistic JSON.
Here are a few patterns worth following:
Match production field names exactly. If the real API returns created_at not createdAt, use the snake_case version in your mock. Mismatched casing is one of the most common sources of silent breakage when transitioning from mock to real.
Include optional and nullable fields. If the real API sometimes returns null for a field, create two mock variants — one where the field is populated and one where it is null — and test both.
Include boundary values. Put one item in the list, put hundreds of items in the list, use a very long string value, use an empty string. These extremes expose layout overflow bugs and missing truncation that normal test data never reveals.
Use ISO 8601 timestamps. Many real APIs return dates in ISO format such as "2026-05-24T14:32:00Z". Using that exact format in your mock ensures your date parsing and formatting code is exercised correctly.
For error responses, include a body that matches what the real API will return. Many APIs wrap errors in a standard envelope — for example:
{
"error": {
"code": "VALIDATION_FAILED",
"message": "The email address is already in use.",
"fields": {
"email": ["Email has already been taken"]
}
}
}Pairing the mock server with the API client
The mock API server and the API client tool complement each other naturally. Use the API client to send a request to your mock route and inspect the exact JSON body, headers, and status code it returns. This makes it easy to spot typos in your JSON or misconfigured status codes before you wire them up in your frontend application code.
A common workflow is to open the mock server in one browser tab, define your routes, then switch to the API client tab and fire requests at the same mock origin to verify responses. Because both tools run in the same browser and the Service Worker is scoped to the origin, requests from the API client are intercepted by the mock exactly as they would be from your application code.
Frequently Asked Questions
How does a browser-based mock server work without Node.js?
The mock server uses a Service Worker to intercept fetch requests from the same browser tab. When your frontend code calls fetch('/api/users'), the Service Worker matches it against your defined routes and returns the configured response — no network request leaves the browser.
Can I test error handling with the mock server?
Yes. Set any route to return a 4xx or 5xx status code. You can also simulate intermittent errors by defining the same route twice with different status codes and toggling between them.
Does it work with frameworks like React or Vue?
Yes. Any fetch or axios call from the same origin tab will be intercepted. This includes calls from React Query, SWR, or any other data-fetching library.
Can I persist my mock routes between browser sessions?
Yes. Routes are saved to localStorage automatically so they survive page refreshes. You can also export your route configuration as JSON and import it later to restore your entire mock setup in seconds.
How do I simulate a slow network or timeout?
Set the delay field on any route to a value in milliseconds. For a typical 3G network feel use 300-600ms. To simulate a near-timeout, use a large delay such as 5000ms combined with a 408 status code so your frontend timeout handler is triggered.
Related Articles & Guides
Mock API Server: Test Your Frontend Without a Backend
There is a scenario that plays out in software teams every single day: the frontend developer is ready to…
Read guide →ArticleMock API Server: Define Fake REST Endpoints in Your Browser
Define mock routes with custom JSON responses, status codes, and delays — test frontend without a backend.
Read guide →BlogAPI Client Guide: Test APIs Online Without Postman
Testing an API should be fast and frictionless. You have an endpoint, you want to fire a request, and you…
Read guide →