Frontend mocks often start with a URL.
That makes sense.
You see a request like this:
GET /api/orders
Then you create a mock for /api/orders, return a response, reload the page, and check the UI.
For simple cases, that works well.
But real frontend work gets messy quickly, because the URL is often not the full state.
One endpoint can mean many states
A single endpoint can represent many different product situations.
For example:
GET /api/orders?status=failed&page=1
GET /api/orders?status=paid&page=1
GET /api/orders?status=pending&page=3
All of these may hit the same path:
/api/orders
But the UI states can be completely different.
One response may show a full list.
One may show an empty state.
One may show a warning.
One may trigger a pagination bug.
One may expose a permissions issue.
The endpoint path is the same, but the frontend state is not.
That is where URL-only mocks start to feel too thin.
Request bodies make this even harder
The problem becomes more visible with POST requests.
A lot of modern APIs reuse one endpoint and put the important state inside the request body:
POST /api/integration/list
{
"documentType": "invoice",
"accountId": "acme",
"includeArchived": false,
"page": 1
}
Change one field and the response may represent a different UI state.
Maybe documentType changes the schema.
Maybe accountId changes permissions.
Maybe includeArchived changes whether the list is empty.
Maybe page exposes a pagination edge case.
If a mock only matches the URL, it cannot distinguish these states.
It can say:
Mock
/api/integration/list.
But it cannot say:
Mock
/api/integration/listonly whendocumentTypeisinvoiceandaccountIdisacme.
That difference matters when you are trying to test a specific frontend behavior.
Staging data does not always help
One common answer is: "Use staging."
Staging is useful. It catches integration problems and gives teams a shared environment.
But staging is not always good at giving you the exact state you need at the exact time you need it.
Maybe staging has data, but you need an empty list.
Maybe the backend is healthy, but you need to test a 500.
Maybe the response is fast, but you need to inspect a loading state.
Maybe you need a specific permission failure, but nobody wants to mutate a test account for one UI check.
Maybe a QA edge case disappears because someone else changed shared test data.
None of this means staging is bad.
It means staging is not always the fastest tool for small frontend state work.
Mock servers are powerful, but sometimes heavy
Mock servers are also useful.
Tools like MSW, Mockoon, WireMock, Postman mocks, Playwright route interception, and Cypress intercepts all have valid places in the stack.
They are especially useful for:
- repeatable test suites
- shared API contracts
- backend-independent development
- team-wide fake environments
- CI workflows
- larger mocking strategies
But sometimes the task is smaller.
You are not trying to design an entire fake backend.
You are staring at one screen and thinking:
I need this one browser request to return this one response so I can see this one UI state.
For that kind of work, a full mock-server setup can be more structure than the problem needs.
The useful mock starts from the real request
This is the product direction behind Ruse.
Ruse is a Chrome extension for local-first API mocking inside the browser.
The current public version, Ruse v0.2.0, is already live in the Chrome Web Store.
Today, the workflow is:
- Open the app you are testing.
- Open Ruse on the working tab.
- Capture a real browser request.
- Create a local mock rule.
- Organize rules into local collections.
- Reload the UI and inspect the state.
No account is required for the current local workflow.
The next quality pass, v0.3.0, is focused on making HTTP rule creation more request-aware.
The goal is not to build a huge platform immediately.
The goal is to make the core browser loop stronger:
capture request -> inspect request -> edit response -> save local mock -> reload UI
Matching the state, not only the path
A better local mock should understand more than the endpoint path.
For many frontend cases, you want to start from the request that actually happened in the browser and decide which parts should matter.
For example, if the captured request body is:
{
"documentType": "invoice",
"accountId": "acme",
"includeArchived": false,
"page": 1,
"clientRequestId": "temporary-random-value"
}
You may want the mock to care about:
{
"documentType": "invoice",
"accountId": "acme"
}
But not care about:
{
"clientRequestId": "temporary-random-value"
}
That is the difference between mocking an endpoint and mocking a state.
The request fields that define the UI state should matter.
The noisy fields should not.
This is not about replacing every testing tool
I do not think browser-level local mocks replace proper tests.
They do not replace contract testing.
They do not replace backend integration testing.
They do not replace staging.
They do not replace a serious mock-server setup when a team needs one.
The value is narrower:
When you need to reproduce one frontend API state quickly, inside Chrome, starting from real browser traffic, local browser-level mocking can be the fastest path.
That is the lane Ruse is being built for.
Practical examples
Here are the kinds of states where URL-only mocks can be too limited:
- Same endpoint, different filters
- Same endpoint, different account
- Same endpoint, different user role
- Same endpoint, different document type
- Same endpoint, different request body
- Same endpoint, different pagination state
- Same endpoint, different feature flag
- Same endpoint, different permission result
The frontend does not care that the path is the same.
The frontend cares what state the response represents.
Where Ruse is going next
Ruse v0.2.0 is live now.
The next development focus is v0.3.0, which is planned as a quality pass around HTTP rule creation:
- Builder-first selected request workflow
- request parameter/body subset matching
- cleaner response body editing
- response status/type/delay controls
- response header cleanup
- clearer mocked logs
- a tighter working-tab permission model, once final QA confirms it
I am deliberately keeping this scoped.
No accounts in v0.3.0.
No billing.
No teams.
No cloud sync.
No dedicated GraphQL or WebSocket editor yet.
The point is to make the core HTTP workflow feel right before expanding the product surface area.
Try the current workflow
If you build frontend against APIs that are hard to control on demand, try Ruse v0.2.0 on one real screen.
Pick one API state that usually slows you down:
- empty list
500- slow response
- malformed JSON
- permission denied
- filtered result
- pagination edge case
Then see whether controlling that state locally in Chrome helps your UI loop move faster.
Website: https://ruse.dev
Chrome Web Store: https://chromewebstore.google.com/detail/becjmbgcgfddoahaoigeeogohpbcmgic?utm_source=item-share-cb
Top comments (0)