🇨🇳 中文版说明 → README.zh-CN.md
Recommend: use version@
v1.0.0+
Make a easy mock-server to mock api, with GET /a/b/c → ./mocks/data/GET/a/b/c.json mapping
- ✔️ Zero intrusion into front-end code
- ✔️ Support https protocol
- ✔️ Support all methods, like
GET,POST,PUT,DELETEetc. - ✔️ Support mock data file type:
json|json5 - ✔️ Support custom middleware in
middleware.{js|cjs|ts}using Koa middleware (the current runtime is Koa 3); Express middleware is also supported withfunc.isExpress=true - ✔️ Support custom route mappings in
routes.{json|json5|js|cjs|ts}; route keys support glob patterns, so multiple requests can reuse the same mock file - ✔️ Support mock logic file type:
js|cjs|ts - ✔️ Support WebSocket simulations, including optional custom logic in
socket.{js|cjs|ts} - ✔️ Support the generation of some simple simulation data, in
mihawk/tools, eg:createRandPhone、createRandEmail
npm i -g mihawkmihawk --port=8888
# mihawk -p 8888then open browser and visit
http://localhost:8888mock data directory:
./mocks/data
./mocks
│
├── /data
│ │
│ ├── DELETE
│ │ ├──/*.{js|cjs|ts} DELETE request resolve logic
│ │ └──/*.{json|json5} DELETE request resolve data
│ │
│ ├── GET
│ │ ├──/*.{js|cjs|ts} GET request resolve logic
│ │ └──/*.{json|json5} GET request resolve data
│ │
│ ├── POST
│ │ ├──/*.{js|cjs|ts} POST request resolve logic
│ │ └──/*.{json|json5} POST request resolve data
│ │
│ └── PUT
│ ├──/*.{js|cjs|ts} PUT request resolve logic
│ └──/*.{json|json5} PUT request resolve data
│
├── middleware.{js|cjs|ts} [optional] custom middleware
├── socket.{js|cjs|ts} [optional] custom WebSocket logic
│
└── routes.{json|json5|js|cjs|ts} [optional] route mappingsmapping:
request : GET http://localhost:8888/a/b/c/d
JSON-file : data/GET/a/b/c/d.json
mock-file : data/GET/a/b/c/d.jsrequest: mock request urlJSON-file: mock origin datamock-file: resolve mock logic, base on origin data
Finally, the return data will be the data after processing mock-file (the mock-file) with origin data (the JSON-file)
A more recommended way to use it is to write all config props into the
.mihawkrc.jsonin the root directoryAnd then run
mihawkin you shell
mihawk initthen edit the
.mihawkrc.jsonto customize your config
{
"host": "0.0.0.0",
"port": 8888,
"https": false,
"cors": true,
"cache": false,
"watch": true,
"mockDir": "mocks",
"mockDataFileType": "json",
"mockLogicFileType": "none",
"autoCreateMockLogicFile": false
}About root config props:
host: string, default0.0.0.0, server listen on this hostport: number, default8888, server listen on this porthttps: boolean | { key: string; cert: string; ca?: string }, defaultfalse. Set it totrueto use the bundled development certificate, or provide certificate file paths. Ifkeyorcertis omitted or its path does not exist, Mihawk falls back to the bundled certificate; other file read errors are propagatedcors: boolean, defaulttrue, iftrue, will addAccess-Control-Allow-Origin: *(and other necessary cors props in headers ) to the response headerscache: boolean, defaultfalse. Iftrue, loaded mock data and logic modules may be reused until their caches are refreshedwatch: boolean, defaulttrue. When started through the CLI, Mihawk watchesmockDir; JSON changes refresh data caches, while logic, template, androutes.{json|js|cjs|ts}changes restart the server. WithmockLogicFileType: "none"andmockDataFileType: "json5", changes toroutes.json5currently require a manual restartmockDir: string, defaultmocks, the directory of mock datamockDataFileType: stringjson|json5, defaultjson, the file type of mock datamockLogicFileType: stringjs|cjs|ts|none, defaultnone, the file type of mock logic (javascriptandtypescriptare also accepted aliases)autoCreateMockLogicFile: boolean, defaultfalse. When logic mode is enabled, create a missing logic file when its route is requestedtsconfigPath: string | null, only used in TypeScript logic mode. If omitted, Mihawk checks<mockDir>/tsconfig.jsonand otherwise uses its built-in TypeScript configurationlogConfig: { ignoreRoutes?: string[] } | null, defaultnull; matching paths orMETHOD /pathpatterns are excluded from request logsbodyParser: { jsonLimit?: string | number; formLimit?: string | number }, request body size limits; defaults are1mbfor JSON and56kbfor URL-encoded formsonBodyParserError: (error: unknown, ctx: KoaContext) => void | Promise, optional request body parsing error handler; see belowsocketConfig: boolean | { stomp?: boolean } | null, disabled by default.trueenables WebSocket on the same HTTP/HTTPS server and port.stompenables STOMP parsing in the built-in handler; a custom socket resolver receives the flag and handles messages itselfsetJsonByRemote: { enable: boolean; target: string; timeout?: number; changeOrigin?: boolean; rewrite?: (path: string) => string; coverExistedJson?: boolean } | null- Default:
undefined - When the local JSON file is missing,
{ enable: true, target: 'https://...' }fetches initial data from the remote service - When the local JSON file already exists, remote data is requested and written only when
coverExistedJson: true; otherwise the local file is used directly - Set it to
null/undefined, or setenable: false, to disable remote loading - Proxy config requires:
target(required): remote server URL, requiredrewrite: optional path rewrite function; use a JavaScript or TypeScript RC file because JSON cannot contain functionstimeout: request timeout in milliseconds, default10000changeOrigin: whentrue, add the target host as a forwardedHostvalue. The original lowercasehostheader is currently retained, so Fetch may combine both valuescoverExistedJson: whentrue, try to overwrite an existing local JSON file on every request; a failed remote request falls back to the local file
- Default:
Remote responses must be JSON objects. Invalid responses and request failures fall back to the JSON template or built-in initial data when the local file is missing. See fallback-remote.md for the exact flow and URL restrictions.
More detail → src/com-types.ts, interface MihawkRC define the config props
bodyParser exposes only jsonLimit and formLimit. Numbers are bytes and must be positive safe integers. Strings accept a number with an optional b, kb, mb, or gb unit (case-insensitive, with optional whitespace between the number and unit); no unit means bytes. Decimal values such as "1.5mb" are accepted only when the resulting byte count is a positive safe integer. Units use powers of 1024. Invalid limits and unknown bodyParser fields are rejected at startup. JSON and URL-encoded form parsing remain enabled; parser selection and strict JSON behavior are unchanged.
For example, add "bodyParser": { "jsonLimit": "2mb", "formLimit": 131072 } to .mihawkrc.json.
Malformed JSON and oversized bodies stop request processing before route logic, custom middleware, or default mocks. The default JSON response contains the error status and its standard HTTP description, such as { "code": 400, "msg": "Bad Request" } or { "code": 413, "msg": "Payload Too Large" }. Unknown errors return { "code": 500, "msg": "Internal Server Error" }. Raw request bodies and parser error messages are not included. If a failed request has not been fully read, Mihawk discards the remaining request data without buffering it, then returns the complete error response so the connection can be reused.
Earlier versions could pass a failed parse through to business logic with the raw body. That behavior is no longer supported. To consume a raw request stream in custom middleware, send a content type that the parser does not handle, such as application/octet-stream. An existing ctx.disableBodyParser = true set before the parser still skips parsing.
Use a JavaScript or TypeScript RC file, or the programmatic API, for onBodyParserError; JSON files can configure only the limits. The callback receives the original error after the default error status and body have been set. It may customize ctx.status and ctx.body, and is awaited. A thrown error or rejected promise returns the generic JSON 500 response above. Business logic exceptions keep their existing error handling.
// .mihawkrc.ts
import type { MihawkRC } from 'mihawk/com-types';
const config: MihawkRC = {
bodyParser: { jsonLimit: '2mb', formLimit: '128kb' },
// Synchronous callbacks are also supported.
onBodyParserError: async (_error, ctx) => {
ctx.body = { error: { status: ctx.status, message: 'Invalid request body' } };
},
};
export default config;Mihawk loads file extensions from the configured data and logic modes:
mockLogicFileType |
Routes file | Custom middleware | Per-route logic | Custom WebSocket logic |
|---|---|---|---|---|
none |
routes.<mockDataFileType> |
Not loaded | Not loaded | Not loaded |
js |
routes.js |
middleware.js |
data/**/*.js |
socket.js |
cjs |
routes.cjs |
middleware.cjs |
data/**/*.cjs |
socket.cjs |
ts |
routes.ts |
middleware.ts |
data/**/*.ts |
socket.ts |
Custom WebSocket logic is loaded only when socketConfig is enabled. WebSocket itself can still run with its built-in handler when mockLogicFileType is none.
Route keys can match either a path such as /users/* or a method-qualified path such as GET /users/*. Rules are checked in declaration order and the first match wins. Route values are paths relative to <mockDir>/data.
Set socketConfig to true to use Mihawk's built-in WebSocket greeting and echo handler. The following TypeScript example also requires mockLogicFileType: "ts" and a mocks/socket.ts file:
import type { SocketResolveFunc } from 'mihawk/com-types';
const resolveSocket: SocketResolveFunc = (socket, request, options) => {
socket.on('message', message => {
socket.send(message.toString());
});
console.log(request.url, options?.clientId, options?.stomp);
};
export default resolveSocket;WebSocket always shares the root host, port, and HTTPS setting. The socketConfig object currently reads only stomp; separate socket host/port settings are not used.
X-Mock-Hit:1when the default file-based mock handled the request;0whenskipDefaultMockreaches it. The header is absent if an earlier middleware ends the response without callingnext()X-Mock-Use-Remote:1when remote data was usedX-Mock-Use-Default:1when built-in initial data was usedX-Mock-Use-Logic: the logic extension (js,cjs, orts) after the converter completes successfully; otherwisenoneX-Mock-Time: downstream processing time measured from thecommonmiddleware; absent if the request completes before reachingcommon
graph LR
A[Dev Mode: Request] --> B(devServer)
B --> D1[Mode 1: mockServer → Local launched mockServer]
B --> D2[Mode 2: Proxy to backend → Change proxy to backend's address]
D1 --> C(Mihawk)
D2 --> E(BackendServer)
F[Production Mode: Request] --> G(BackendServer)
style A fill:#2c2c2c,stroke:#ccc,fill-opacity:1,color:#eee
style B fill:#5e6472,stroke:#f0f0f0,fill-opacity:1,color:#f0f0f0
style C fill:#09c,stroke:#f0f0f0,fill-opacity:1,color:#f0f0f0
style D1 fill:#5e6472,stroke:#f0f0f0,fill-opacity:1,color:#f0f0f0
style D2 fill:#5e6472,stroke:#f0f0f0,fill-opacity:1,color:#f0f0f0
style E fill:#7a6da2,stroke:#f0f0f0,fill-opacity:1,color:#f0f0f0
style F fill:#2c2c2c,stroke:#ccc,fill-opacity:1,color:#eee
style G fill:#7a6da2,stroke:#f0f0f0,fill-opacity:1,color:#f0f0f0
classDef devStyle fill:#2c2c2c,stroke:#ccc,fill-opacity:1,color:#eee;
classDef serviceStyle fill:#5e6472,stroke:#f0f0f0,fill-opacity:1,color:#f0f0f0;
classDef backendStyle fill:#7a6da2,stroke:#f0f0f0,fill-opacity:1,color:#f0f0f0;
class A,F devStyle
class B,D1,D2 serviceStyle
class C,E,G backendStyle
In the above diagram,
devServeris typically provided by bundling tools during local development, such as Vite or Webpack, which have corresponding configuration options.
Essentially, it is based on the proxy functionality of
devServer, forwarding requests to the mihawk server.
in vite.config.js file:
import { defineConfig } from 'vite';
export default defineConfig({
server: {
proxy: {
'/api': {
target: 'http://localhost:8888', // mihawk server address
changeOrigin: true,
rewrite: path => path.replace(/^\/api/, ''),
},
},
},
});in webpack.config.js file:
// webpack.config.js
module.exports = {
devServer: {
proxy: {
'/api': {
target: 'http://localhost:8888', // mihawk server address
changeOrigin: true,
pathRewrite: { '^/api': '' },
},
},
},
};For request GET /api/fetch_a_random_number,it return response with random number data
{
"code": 200,
"data": 123456,
"msg": "success"
}You cal aslo dont do this step, coz the mock data file is auto create when request a not exists file
Now, if request GET /api/fetch_a_random_number,return data is 123456, it is fixed data
First set mockLogicFileType to js in .mihawkrc.json; otherwise Mihawk does not load .js logic files. Then create the file with the following content:
module.exports = async function (oldJson) {
oldJson.data = Math.floor(Math.random() * 1000000); // generate random number
return oldJson; // return data, it is required
};Start mihawk server now, if request GET /api/fetch_a_random_number,return data is random number, each request return a different data
About MockLogic File:
- Both support
js|cjs|ts, the process is same。Attention toexport defaultis necessary intsfile!autoCreateMockLogicFiledefaults tofalse. When logic mode is enabled, set it totrueto create a missing mock logic file when its route is requested- Of course, it is worth mentioning that MockLogic files aren't necessary files. If there is no logical demand for data processing, using only JSON files can also simulate the request
This example requires mockLogicFileType: "ts" and the file name mocks/routes.ts.
/**
* mihawk's routes file:
*/
const routes: Record<string, string> = {
'GET /test': './GET/test',
'GET /test-*': './GET/test', // key: routePath,support glob expression; value: mock data file path (no ext)
};
//
export default routes;This example requires mockLogicFileType: "ts" and the file name mocks/middleware.ts.
/**
* mihawk's middleware file:
* - a Koa middleware (Mihawk currently uses Koa 3)
*/
import type { KoaContext, KoaNext } from 'mihawk/com-types';
/**
* Middleware functions, to implement some special data deal logic,
* - This function exec before the default-mock-logic. Simply return or don`t call "await next()" could skip default-mock-logic
* - This function is a standard KOA middleware that follows the KOA onion ring model
* - see more:https://koajs.com/#middleware
* @param {Context} ctx
* @param {Next} next
* @returns {Promise<void>}
*/
export default async function middleware(ctx: KoaContext, next: KoaNext) {
// do something here
console.log(ctx.url);
if (ctx.path === '/diy') {
ctx.body = 'it is my diy logic';
} else {
await next(); // default logic (such like mock json logic)
}
}Set
middleware.isExpress=trueto explicit definition a express middleware function before export, if you write in express-stype Other complex custom middleware examples based on@koa/routerandkoa-compose: middleware.md
'use strict';
/**
* GET /xxx
* This file isn’t mandatory. If it is not needed (such as when there is no need to modify response data), it can be deleted directly
*/
/**
* Mock data resolve function, the original data source is the JSON file with the same name as this file
* @param {object} originData (mocks/data/GET/xxx.json)
* @param {MhkCvtrExtra} extra { url,method,path,query,body }
* @returns {object} newData
*/
export default async function convertData(originData: Record<string, any>, extra: Record<string, any>) {
// write your logic here...
originData.newProp = 'newPropXxx';
return originData; // return data, it is required
}Customize the initial content of auto-created JSON files via EJS templates, see json-data-tpl.md
- Mockjs is a front-end mockjs library that provides powerful simulated data generation capabilities
- Mihawk is a Node.js mock service that can be used with front-end projects or standalone; it provides mock capabilities for httpServer/SocketServer based on Nodejs
- Mockjs intercepts requests and returns simulated data by hijacking xhr/fetch, etc., which requires certain modifications to front-end engineering code, and there are some differences in the request sending/receiving process compared to the real online environment
- Mihawk handles requests through a local Koa server (the current runtime is Koa 3) without modifying front-end request code. Requests use the normal HTTP/WebSocket transport, while response status, headers, and data still follow the configured mock behavior
- Mockjs is used for simulated data production, generating corresponding fake data through its specific syntax
- Mihawk is used to simulate a BackendServer based on Node.js, such as WebSocket and HTTP servers, combined with simple data generation functions to create fake data
mihawk/tools: Built-in utility functions likecreateRandXxxfor generating fake data; this functionality is not as powerful as Mockjs- Consider using both mockjs's
data generateand mihawk'sserver mocktogether; they are not mutually exclusive
- Consider using both mockjs's
your_project/mocks/middleware.ts: Simulate backend services, such as httpServeryour_project/mocks/socket.ts: Simulate backend services, such as socketServer