mock turns REST Client-style .http files into a local HTTP server with hot
reload and a request-log UI. It is a single binary for the gap between
hand-written test doubles and a real backend — not a replacement for WireMock,
Prism, or Mockoon, and not the Go unit-test mocking libraries (gomock, mockery).
Requires Go 1.26 or newer:
go install github.com/sspencer/mock@v0.1.4
mock -versionBinaries for Linux, macOS, and Windows are on the Releases page. Docker instructions are below. To build from a clone, see Building From Source.
This binary is named mock. If you already have another mock on your PATH,
the new one will shadow it or be shadowed, depending on install order.
Run the example API:
go run . examples/user.httpThe server prints a short banner with the listen address, the admin UI URL, and
whether it is watching files, then the route list, and listens on 127.0.0.1:8080
by default:
starting mock HTTP server on 127.0.0.1:8080
admin UI at http://127.0.0.1:8080/mock/
watching request files for changes
Available mock methods:
GET / Return home page external html file
POST /users Create random user
GET /users/:id Return any user
While it is running, it watches the given .http file(s) and any $file
response bodies they reference. Save a file and the server reloads the routes
and reprints the updated method list. A failed reload keeps the previous routes
and logs the error.
curl http://localhost:8080/users/42
curl -X POST http://localhost:8080/users
curl http://localhost:8080/names?type=catOpen the request console at http://localhost:8080/mock/. The responsive UI shows each request and response with raw HTTP-style details, a collapsible live-routes panel, filter/pause/clear controls, HAR export, light and dark themes, and a Help dialog that explains those controls.
To populate the console during UI development, start the server with
examples/user.http, then run this Bash/curl script in another terminal:
./examples/send-requests.sh # MOCK_PORT, or 8080 if unset
./examples/send-requests.sh -p 9000 # overrides MOCK_PORTIt sends 15 requests across the example routes, including JSON request bodies,
the rotating 201/400 responses, a 204 deletion, and the delayed response.
It prints status/timing summaries and leaves response bodies for inspection in
the console. Rerun it whenever you need more traffic.
Dark-mode request console populated with examples/send-requests.sh, showing a successful POST request and its response.
You can also pipe a request file through stdin:
cat examples/user.http | go run .Common development commands:
make test
make all
make buildmake build always writes the mock binary into GOBIN, or GOPATH/bin when
GOBIN is not set. make / make all does the same after tests pass, even if
Make thinks the sources are unchanged. The binary reports git describe via
-version when built this way.
After building:
mock -p 9090 -b 127.0.0.1 -l inspect examples/user.httpThat serves the mock API on 127.0.0.1:9090 and the request log at
http://127.0.0.1:9090/inspect/.
mock -versionBuild the image:
docker build -t mock .Run it with a .http file from your host machine. Containers must bind
0.0.0.0 so published ports are reachable from the host (-b defaults to
localhost):
docker run --rm \
-p 8080:8080 \
-v "$PWD/examples/user.http:/mock/user.http:ro" \
mock -b 0.0.0.0 /mock/user.httpThe bind mount keeps the request file outside the image. Rebuild the image only
when the mock binary or static UI changes. Because the file is mounted from
the host, edits to the host .http file are picked up by the running container
and reloaded automatically.
If the request file uses $file response bodies, mount the whole directory so
relative file references are available inside the container:
docker run --rm \
-p 8080:8080 \
-v "$PWD/examples:/mock/examples:ro" \
mock -b 0.0.0.0 /mock/examples/user.httpmock [flags] <file.http> [file.http...]
mock [flags] <directory>
cat file.http | mock
mock -version
Flags:
| Flag | Default | Description |
|---|---|---|
-p |
8080, or MOCK_PORT |
HTTP port |
-b |
127.0.0.1 |
Bind address. Use 0.0.0.0 to listen on all interfaces |
-l |
mock |
URL path for the request-log UI (/mock/) |
-cors |
(off) | Access-Control-Allow-Origin value (* or an origin). Only browser preflight OPTIONS are short-circuited |
-cert / -key |
(off) | Enable HTTPS with the given certificate and key |
-version |
Print version and exit |
If -p is omitted, mock uses the MOCK_PORT environment variable when it
is set. An explicit -p flag always wins.
You can pass one or more .http files. When no files are passed, mock reads
from stdin. Empty input fails fast with an error that points at the expected
request-section format.
Request files passed on the command line are watched for changes, including
relative $file response bodies. On save, mock reloads those files, swaps in
the new routes without restarting the process, and prints the updated route list.
SIGINT/SIGTERM shut the server down cleanly and stop the file watcher.
If you pass a single directory, mock serves that directory as a static file
server from / instead of loading mock routes or the request-log UI.
Headers after the request line are response headers. To match incoming
request headers such as Authorization, Cookie, or Accept, use
# $header.Name=value above the request line — not a header below it.
Content-Type after the request line is the usual way to set the mock response
content type. mock warns at load time if a well-known incoming header is used
as a response header without a matching $header.* matcher.
Each response starts with ###, followed by a name, optional variables, an HTTP
request line, optional response headers, a blank line, and an optional body.
### Return user
GET /users/:id
Content-Type: application/json
{
"id": "{{$id}}",
"name": "{{$name}}",
"requestId": "{{$uuid}}"
}
### Delete user
# $status=204
DELETE /users/:idThe request target may be a path or a full URL. Only the path and query string are used for matching.
mock supports a practical subset of JetBrains REST Client / .http files:
| Supported | Not supported |
|---|---|
### named sections |
@name / request separators beyond ### |
# $var=value control variables |
Environment files / {{env}} from IDE |
Request line METHOD /path |
Full multi-step scripts |
| Response headers after the request line | Separate request vs response documents |
# $header.Name=value request header matchers |
Body-content matchers |
{{$placeholder}} in bodies and response headers |
Imports of other .http files |
$file relative body files |
Absolute $file paths |
Inline bodies normalize line endings to LF and discard trailing blank lines.
A line beginning ### is always a section boundary, including within a body.
Use a $file dependency for content that must preserve line endings, trailing
whitespace, or literal section delimiters. Individual inline lines are limited
to 1 MiB. Malformed queries, invalid headers, and invalid control directives
produce source-and-line errors; a failed reload retains the working routes.
Variables live in comments directly below the ### line:
# $status=201
# $delay=500ms
# $file=users.json
# $header.Authorization=Bearer secretSupported control variables:
$status: response status code. Defaults to200. The supported final status range is200–999; invalid values are load errors.$delay: response delay parsed with Go duration syntax, such as250msor2s. Negative or invalid durations are load errors.$file: response body file, resolved relative to the.httpfile.$header.Name=value: require the incoming request to include that header. Use*as the value to accept any non-empty header.
$file paths must be relative and cannot contain .. path segments. If no
explicit Content-Type header is set, file-backed responses infer it from the
file extension when possible. Placeholder expansion requires a text, JSON, XML,
or JavaScript content type (explicit or inferred) and valid UTF-8 text without
binary control bytes. Other files are served as raw bytes. Symlinks cannot escape
the directory containing the .http file.
Response bodies and response headers can contain {{$name}} placeholders.
mock resolves them from:
- Path parameters, such as
:idin/users/:id. - Query parameters, such as
typein/names?type=cat. - Variables declared in comments, such as
$delay. - Built-in generated values.
Path parameters take precedence over query parameters with the same name. Variable names may contain dots and hyphens after their first character. Unknown placeholders remain literal and produce load-time warnings; query parameters supplied only at request time can resolve those warnings at runtime. Inside JSON strings, inserted values are JSON-escaped. Outside strings, values are inserted literally, so numeric and boolean placeholders retain their types. Response headers containing invalid characters after expansion return an error.
Useful generated values:
{{$name}} {{$firstName}} {{$lastName}}
{{$user}} {{$email}} {{$phone}}
{{$url}} {{$server}} {{$hash}}
{{$bool}} {{$integer}} {{$float}}
{{$uuid}} {{$guid}} {{$timestamp}}
{{$isoTimestamp}} {{$file}} {{$sentence}}
{{$paragraph}} {{$article}}
Generated values are random faker data and are recalculated each time a response body is rendered.
Unknown placeholders resolve to an empty string.
Routes match on HTTP method, path, any query parameters declared in the
.http file, and any $header.* matchers.
### Cat names
GET /names?type=cat
Content-Type: application/json
{"type":"{{$type}}","names":["miso","taco"]}GET /names?type=cat matches. GET /names?type=dog does not.
Path parameters are introduced with :.
### User profile
GET /users/:id/profile
Content-Type: application/json
{"id":"{{$id}}"}### Secure read
# $header.Authorization=Bearer secret
GET /secure
Content-Type: application/json
{"ok":true}If more than one response has the same method and URL (including across multiple
input files, in load order), mock rotates through the matching responses.
This is useful for retry paths and stateful client behavior without building a
stateful fake server.
### First create succeeds
# $status=201
POST /users
Content-Type: application/json
{"id":1}
### Second create fails
# $status=400
POST /users
Content-Type: application/json
{"error":"duplicate user"}Repeated POST /users requests return 201, then 400, then 201 again.
Use Reset responses to restart rotation counters. Clear log only removes traffic.
The UI is mounted under -l (default /mock/):
| Path | Purpose |
|---|---|
/mock/ |
Request log UI |
/mock/events |
Server-sent events stream (with event id / Last-Event-ID) |
/mock/clear |
POST clears stored events without changing rotation counters |
/mock/reset |
POST resets response sequences without clearing traffic |
/mock/state |
GET active revision, route count, last successful reload, and reload error |
/mock/routes |
GET route configuration, including source location, match headers, status, delay, and body preview |
Path conflicts: the admin mount is reserved and takes precedence over mock
routes beneath it. Keep API routes outside that mount, or change -l.
UI features: theme toggle, filter, pause stream, independent clear/reset controls,
HAR export, a Help dialog, and a routes panel that refreshes after hot-reload.
Both administrative POST endpoints require X-Requested-With or JSON
Content-Type (or a same-origin browser request), and reject cross-origin requests.
Drag the vertical divider to enlarge the traffic list or inspector. Horizontal dividers resize traffic versus routes and request versus response. Divider sizes are remembered locally. Keyboard users can focus a divider and use arrow keys, Shift+arrow for larger steps, or Home/End. Double-click restores the default. On narrow screens, selecting traffic or a route opens the inspector; use Back to traffic & routes to return to the list.
- Pretty / Raw formats valid, complete JSON or shows the original captured content. Headers remain visible, and binary/truncated bodies are labeled.
- Copy body copies original captured text; binary captures offer Copy base64.
- Copy cURL produces a shell-quoted command including captured headers and complete text bodies. Incomplete or binary request bodies show an explanation instead of producing a misleading replay command. Copied credentials are included.
- Matched requests show the fixture name, source file/line, configuration revision, selected response position, and elapsed time. Unmatched requests show up to five nearby routes with method, path, query, and header mismatch explanations.
- Select a configured route to inspect its match requirements, response headers, variables, status, delay, and body template or file reference. File contents are not loaded into the route preview; large inline previews are truncated.
- Failed reloads retain active routes and show an error banner. The banner is hidden during normal operation to leave more room for traffic and details. Pause freezes the table while buffering the latest 1,000 new requests. Resume shows that traffic; the status indicates how many older paused requests were omitted. The Clear log button clears server history across connected tabs without changing response rotation. Traffic arriving after the clear boundary remains.
SSE cursors include a server session and sequence number. Slow subscribers
reconnect to replay retained events; a history gap or restart emits a reset
event before replay. Clear emits a clear event. Request events include the selected route,
sequence position, and configuration revision (or mismatch candidates). History is limited to 1,000
events; gaps are reported rather than silently hidden. Event bodies include
separate byte counts, truncation metadata, and base64 encoding for binary data.
Export HAR downloads captured requests, including their actual HTTP/HTTPS scheme, byte sizes and binary response encoding. Capture is limited to 64 KiB per body. HAR extension fields mark truncated bodies and binary requests; these are diagnostic captures, not complete replay fixtures. Only total server handler time is measured, so individual network timing phases remain unknown.
mock -cors '*' examples/user.http
mock -cert cert.pem -key key.pem -p 8443 examples/user.http-cors adds CORS headers to mock responses (or static-directory responses),
including Vary: Origin. Browser preflights (OPTIONS with
Access-Control-Request-Method) return 204 and reflect requested headers.
Other OPTIONS requests run the mock route. Admin endpoints are excluded from
CORS, including when -cors '*' is used. Cross-origin clear requests are rejected.
With -cert/-key, the startup banner prints https:// for the admin UI.
Binding to all interfaces (-b 0.0.0.0) prints a warning: the admin UI is
unauthenticated and request logs may include Authorization headers and bodies.
Normal HTTP writes have a 30-second budget beginning after any configured response delay. SSE writes have a renewable 10-second deadline and idle streams send heartbeats. Shutdown stops accepting connections and allows 400 ms for active requests before closing remaining connections.
This repository is intentionally small:
main.go/watch.go/version.gowire CLI flags, input loading, watching, and lifecycle.restclient/parses.httpfiles.mockhttp/matches requests, renders responses, and streams request-log events.static/contains the request log UI (embedded at build time).examples/contains request files you can run locally.docs/recommendations.mdtracks improvement history.
Before sending a change around:
make verifymake fmt formats Go files explicitly; verification never rewrites them.
make all also installs the binary. Dashboard tests require Node.js 22+.
See CONTRIBUTING.md for setup and pull-request notes. Release history is in CHANGELOG.md.
