DEV Community

Kamran Khan
Kamran Khan

Posted on

My demo runs with no backend: a mock axios client that mirrors the real API

The hardest part of shipping a product demo isn't the demo itself. It's that the demo needs a backend, and backends need servers, and servers need babysitting. I wanted a demo page anyone could click through — login, dashboard, user management — with zero infrastructure behind it.

So I built a fake API that mirrors the real one. One seam, and the entire frontend switches over without knowing.

In client.js, the axios client is created behind a single branch:

const client = isDemoMode() ? createDemoClient() : axios.create({
  baseURL: `${import.meta.env.VITE_API_URL || 'http://localhost:8000'}/api`,
  headers: { Accept: 'application/json' },
});
Enter fullscreen mode Exit fullscreen mode

isDemoMode() just reads VITE_DEMO_MODE=true from the build. The public preview build sets it; the shipped product doesn't. Everything else in the app — AuthContext, the pages, the components — imports the same client and calls the same endpoints: POST /auth/login, GET /auth/me, GET /dashboard/stats, apiResource users.

The mock, demoMock.js, implements those endpoints against in-memory arrays. The critical discipline: it returns the exact response shapes the real API returns. POST /auth/login returns { token, user }. The user object carries id, name, email, role, created_at — same fields as the Laravel UserResource. Validation failures come back as 422-shaped errors with an errors object.

Why this matters more than it sounds: if the mock drifted from the real API — say it returned username where Laravel returns name — the demo would silently test a frontend that doesn't match the product. Mirroring the response shapes means the demo is also a regression test for my frontend's assumptions. When I change the real API, the demo breaks visibly, and I fix the mock in the same commit.

I seed it with an admin (admin@demo.io) plus eight sample users, and mutations go to the in-memory array — you can create and delete users in the demo and it all "works", resetting on reload. Honest limits: the demo banner says data is simulated. No fake permanence.

The pattern generalises beyond demos. Anytime your frontend depends on an API you don't want to run — a third-party sandbox, a service under development, a test environment — write the mock to the response-shape contract, not to vibes. Your future self, debugging why the mock behaves differently, will thank you.

This exact setup ships in my Laravel + React SaaS starter kit, so a buyer can try every flow in the live preview and then run the same code against the real Laravel API: https://kamranofficial.gumroad.com/l/mhekoig ($19, one-time).

Top comments (0)