Mokup: A Build-Tool-Friendly Visual Mocking Tool for Vite, Webpack, Node.js, and Workers

Published: (February 13, 2026 at 12:20 AM EST)
5 min read
Source: Dev.to

Source: Dev.to

What is Mokup

Logo

Mokup is a file‑based HTTP mocking tool.
You put mock files in a mock/ directory, and Mokup automatically scans them, builds matching routes, and returns responses.

Its goal is straightforward: help mocks run quickly inside your existing frontend project and reduce the cost of building a separate service just for API collaboration.

Key features

  • Build‑tool friendly – works with both Vite and Webpack without forcing a project rewrite.
  • Visual debugging – built‑in Playground shows route status at a glance.
  • Great developer experience – mock file and config updates refresh automatically, without frequent restarts.
  • Deployable to multiple environments – local dev, Node.js server, Workers, and Service Workers.

Why I built it

For many teams, the pain is not writing mocks; it’s the surrounding workflow:

  1. Too many setup steps and re‑configuration whenever the build tool changes.
  2. Poor visibility during local debugging – people end up guessing by reading files.
  3. Slow feedback loops when every mock change requires a restart or manual verification.

Mokup solves these three issues: lighter setup, stronger visualization, and faster feedback.

Build‑tool friendly

Vite integration

// vite.config.ts
import mokup from 'mokup/vite'

export default {
  plugins: [
    mokup({
      entries: { dir: 'mock', prefix: '/api' },
    }),
  ],
}

After adding files under mock/, Mokup scans them and generates routes automatically.
You can also open the Mokup Playground from your CLI output for visual debugging.

CLI screenshot

Webpack integration

// webpack.config.js
const { mokupWebpack } = require('mokup/webpack')

const withMokup = mokupWebpack({
  entries: { dir: 'mock', prefix: '/api' },
})

module.exports = withMokup({})

This lets you add mock capability into your existing build pipeline without changing your business‑code structure.

Visualization: Playground

Mokup includes a built‑in Playground to inspect scanned routes, methods, paths, and config chains.

Default entry in Vite dev:

http://localhost:5173/__mokup

Online demo:

Playground entry

Why it matters – When an endpoint does not work, you don’t need to grep everywhere. Open the Playground page and instantly see whether the route was scanned, disabled, and which config matched.

Developer experience: hot‑reload support

In Vite dev, Mokup watches the mock directories and refreshes the route table automatically. Typical changes that trigger hot reload include:

  • Add / modify / delete route files, e.g.
    • mock/users.get.ts
    • mock/messages.get.json
    • mock/orders/[id].patch.ts
  • Modify directory config filesmock/**/index.config.ts
  • Change directory structure – move, rename, or create nested folders

After a change, the Playground refreshes the route list automatically, making debugging much faster.

If you do not need file watching, set watch: false in entries.

Quick example: from file to response

// mock/users.get.ts
import { defineHandler } from 'mokup'

export default defineHandler({
  handler: c => c.json([{ id: 1, name: 'Ada' }]),
})

Start dev and visit /api/users (assuming prefix: '/api'). You’ll receive the mock data.

Mock directory tree

Quick integration with @faker-js/faker

Mokup handlers are plain TS/JS functions, so you can integrate with @faker-js/faker directly, without any extra adapter layer.

// mock/users.get.ts
import { faker } from '@faker-js/faker'
import { defineHandler } from 'mokup'

export default defineHandler(c => {
  const size = Number(c.req.query('size')) || 5
  const users = Array.from({ length: size }).map(() => ({
    id: faker.datatype.uuid(),
    name: faker.person.fullName(),
    email: faker.internet.email(),
  }))
  return c.json(users)
})

Now a request to /api/users?size=10 returns a list of 10 randomly generated users.

Give Mokup a try!

  • GitHub:
  • Docs / Playground:

Happy mocking!

const size = Number(c.req.query('size') ?? 10)
const count = Number.isNaN(size) ? 10 : Math.min(Math.max(size, 1), 50)
const list = Array.from({ length: count }, () => ({
  id: faker.string.uuid(),
  name: faker.person.fullName(),
  email: faker.internet.email(),
  city: faker.location.city(),
  createdAt: faker.date.recent({ days: 30 }).toISOString(),
}))

return c.json({
  list,
  total: 200,
  page: 1,
  pageSize: count,
})

This is very useful for list, search, and detail page integration.

If you need reproducible results, add faker.seed(123) at the top of the handler.

Deployable to multiple environments

Node.js dev mode example

import { createFetchServer, serve } from 'mokup/server/node'

const app = await createFetchServer({ entries: { dir: 'mock' } })
serve({ fetch: app.fetch, port: 3000 })

Cloudflare Worker example

import { createMokupWorker } from 'mokup/server/worker'
import mokupBundle from 'virtual:mokup-bundle'

export default createMokupWorker(mokupBundle)

Note: virtual:mokup-bundle is only available in Vite with @cloudflare/vite-plugin.
In Node.js dev mode, use createFetchServer directly; you do not need that virtual module.

Core architecture

architecture

Use cases and boundaries

Good fit

  • Teams with existing Vite/webpack projects that want low‑cost mock integration
  • Projects that need visual route diagnostics
  • Workflows that value fast feedback after mock updates

Less suitable

  • Scenarios that depend heavily on complex dynamic proxy chains
  • Extremely lightweight setups that do not want build‑time or plugin‑based integration

Mokup is not trying to replace every mock solution. It is designed to make mocks easier to adopt, easier to debug, and better aligned with everyday development workflows.

Closing

Mokup is still evolving quickly. Feedback is very welcome, including feature requests, DX feedback, and documentation improvements.

If this is useful for your workflow, feedback and feature requests are welcome.

0 views
Back to Blog

Related posts

Read more »

Inertia.js Silently Breaks Your App

TL;DR After weeks in a production Laravel 12 + React 19 + Inertia v2 app, I repeatedly hit failure modes that were expensive to diagnose: overlapping visit can...

Show HN: Simple org-mode web adapter

Org Web Adapter A lightweight local web app for browsing and editing Org files. The app is implemented as a single Python server main.py plus one HTML template...