A while ago, I was deep into building a new frontend project.
I had spent hours polishing the interface — the form validation was tight, the loading states looked smooth, and I was finally ready to test the full CRUD (Create, Read, Update, Delete) workflow.
The backend wasn't ready yet, so like any developer, I reached for a popular free dummy API to wire up my requests.
I filled out the creation form, clicked "Submit", and opened Chrome DevTools to see what happened.
The Network tab lit up green: POST /posts → 201 Created with { id: 101, title: "My New Post" }.
I felt that quick spike of dopamine. It worked!
...Or so I thought.
I navigated back to the dashboard to watch the new item render in the list. Nothing was there.
I refreshed the page. Still nothing.
I manually fired a GET /posts/101 — and got hit with a 404 Not Found.
Then I tested the delete button: DELETE /posts/1 returned 200 OK, but the second I reloaded the browser, that exact same post was right back in my table, completely untouched.
The Moment I Realized How Broken This Was
I sat back in my chair, staring at the screen, genuinely annoyed.
Here we are in modern web development with sophisticated frontend frameworks, reactivity, and query caching — yet every public mock API we use still behaves like a dumb echo chamber from 2012. They take your JSON payload, slap a fake id: 101 on it, pretend everything went great, and instantly discard your data into a black hole.
To test a simple button in my UI, I was suddenly stuck with two bad options:
- Stop frontend work and spend 3–4 hours building a throwaway backend: Spin up Node.js, Express, SQLite, Docker, and write migrations just to test a prototype.
-
Litter my codebase with fake client-side hacks: Write temporary
useStateoverrides or mocklocalStorageadapters that don't test real HTTP headers, status codes, or network behavior — code I'd later have to untangle and rip out.
I had run into this exact wall on almost every side project, client MVP, and tutorial I'd ever worked on.
And that night, I couldn't stop asking myself:
"Why hasn't anyone built a mock API that actually remembers what you send it? Why can't each developer get an isolated, zero-config sandbox in the cloud that persists POST, PATCH, and DELETE requests across their session?"
I waited for a tool like that to exist. When I couldn't find one, I decided to build it myself: Playground API.
The idea was simple: build a stateful REST and GraphQL sandbox that acts like a real production backend. When you create an item, it stays created. When you update a field, it stays updated. When you delete a record, it's gone — all isolated to your browser session with zero credentials, zero databases, and zero config.
Here is why that changes everything for frontend prototyping.
The Illusion of Memory in Frontend Prototyping
When testing frontend applications, there are three common workarounds developers use to fake backend memory:
-
Local Component State (
useState/Pinia/Vuex): You store mock data in client-side arrays. Every time you refresh the page or open a new browser tab, all your modifications reset to initial mock constants. -
localStorage/ IndexedDB Mocks: You write custom adapter wrappers that save items tolocalStorage. While this persists across reloads, it does not test actual HTTP serialization, headers, HTTP status codes, or asynchronous server latency. -
Disposible Local Servers (
json-server/ Express): You run a local server file on your machine. This works, but it cannot be easily shared with team members, designers, or QA testers reviewing your pull request deployment on Vercel or Netlify.
None of these approaches deliver the experience frontend engineers actually want: a live, hosted cloud API that remembers mutations per session without requiring local database setup.
What "Stateful API Mocking" Looks Like in Action
A stateful mock API bridges the gap between static dummy JSON and full production backends. It functions by creating an isolated sandbox for your browser session.
Here is the exact lifecycle:
sequenceDiagram
autonumber
actor Dev as Frontend Client
participant API as Playground API Sandbox
Dev->>API: 1. POST /posts { title: "New Feature Launch" }
API-->>Dev: 201 Created { id: 101, title: "New Feature Launch" }
Note over API: Stored in caller's Virtual Session Overlay
Dev->>API: 2. GET /posts/101
API-->>Dev: 200 OK { id: 101, title: "New Feature Launch" }
Dev->>API: 3. PATCH /posts/101 { title: "Updated Title" }
API-->>Dev: 200 OK { id: 101, title: "Updated Title" }
Dev->>API: 4. DELETE /posts/101
API-->>Dev: 200 OK { message: "Resource deleted" }
Dev->>API: 5. GET /posts/101
API-->>Dev: 404 Not Found
Every standard HTTP verb functions exactly as it would on a production server:
-
POST→GET: Newly created records immediately appear in collection lists and single-resource queries. -
PATCH/PUT→GET: Updated fields reflect on subsequent queries. -
DELETE→GET: Deleted items return404 Not Foundand are excluded from pagination counts.
Step-by-Step Code Walkthrough
Let's test this directly against Playground API by Niles Labs. You can run this directly in your browser console or Node.js environment:
const BASE = 'https://playground.nileslabs.com/api/v1';
async function runStatefulDemo() {
// Step 1: Create a Todo item
console.log('--- 1. Creating a Todo ---');
const createRes = await fetch(`${BASE}/todos`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
title: 'Review PR #204',
completed: false,
user_id: 1,
}),
});
const createdTodo = await createRes.json();
console.log('Created:', createdTodo);
// Step 2: Retrieve the newly created Todo by ID
console.log('\n--- 2. Fetching Created Todo by ID ---');
const getRes = await fetch(`${BASE}/todos/${createdTodo.id}`);
const fetchedTodo = await getRes.json();
console.log('Fetched:', fetchedTodo);
// Step 3: Toggle the completion state via PATCH
console.log('\n--- 3. Updating Todo via PATCH ---');
const patchRes = await fetch(`${BASE}/todos/${createdTodo.id}`, {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ completed: true }),
});
const updatedTodo = await patchRes.json();
console.log('Updated Status:', updatedTodo.completed); // true
// Step 4: Delete the Todo
console.log('\n--- 4. Deleting the Todo ---');
const deleteRes = await fetch(`${BASE}/todos/${createdTodo.id}`, {
method: 'DELETE',
});
console.log('Delete status:', deleteRes.status); // 200
// Step 5: Verify it is gone
console.log('\n--- 5. Verifying Deletion ---');
const verifyRes = await fetch(`${BASE}/todos/${createdTodo.id}`);
console.log('Status on deleted item:', verifyRes.status); // 404 Not Found
}
runStatefulDemo();
Multi-Tab & Multi-Client Session Isolation
A common question is: If multiple developers or automated test suites use the API simultaneously, will their POST requests overwrite each other?
No. Stateful sandbox engines use session isolation:
- In Browser: An HTTP-only session cookie automatically identifies each browser sandbox.
-
In CI/CD & Automated Tests (Playwright / Cypress): You can pass a custom header
X-Playground-Identity: test-runner-suite-1to maintain an isolated sandbox across parallel test runners. -
Resetting State: Whenever you want a clean slate, a simple
DELETE /session/resetrequest flushes your session overlay and restores the default seed dataset.
Why This Changes Frontend Development
When your mock API behaves like a real backend:
- Interactive Client Demos Work: You can send a live preview link (e.g. on Vercel) to stakeholders or clients, and they can click around, create posts, toggle todos, and delete items without finding broken empty states.
- Realistic Query Invalidation: Tools like React Query, SWR, and Redux Toolkit Query behave naturally when invalidating query caches.
- No Database Maintenance: You spend zero minutes configuring Docker, spinning up Postgres instances, or writing migration scripts for throwaway prototypes.
Conclusion
Mock APIs should do more than echo your requests. By remembering mutations across the entire HTTP lifecycle, stateful sandboxes make frontend prototyping feel authentic and production-ready from the very first commit.
To experiment with persistent mutations in your next application, start testing with Playground API by Niles Labs.
Top comments (0)