HTTP mock server written in Go. Register canned responses for any method/path combination, then hit those paths to get the registered responses back.
# Default port 8080
docker run -p 8080:8080 kaleman14/mockserver:latest
# Custom port
docker run -e PORT=3000 -p 3000:3000 kaleman14/mockserver:latestThe binary also supports a -healthcheck flag for container health checks:
mock-server -healthcheck| Environment Variable | Default | Description |
|---|---|---|
PORT |
8080 |
HTTP listen port |
All control endpoints are under the /__mock/ prefix.
Register a single mock rule.
Request body:
{
"method": "GET",
"path": "/api/users",
"response": {
"status_code": 200,
"headers": { "X-Custom": "value" },
"body": { "users": [] }
}
}| Field | Type | Default | Description |
|---|---|---|---|
method |
string | POST |
HTTP method to match |
path |
string | required | URL path to match |
response.status_code |
int | 200 |
Response status code |
response.headers |
object | none | Response headers |
response.body |
any | required | Response body (returned as-is) |
response.times |
int | unlimited | Number of times this rule should match before being removed. 0 or omitted means unlimited. |
Response:
{ "ok": true, "key": "GET /api/users" }Multiple rules can be registered for the same method+path. They are matched in FIFO order. When a rule with a times limit is exhausted, the next rule in the queue is used.
Register multiple mock rules in a single request. Takes an array of the same objects as /__mock/set.
Request body:
[
{ "method": "GET", "path": "/a", "response": { "body": "ok" } },
{ "method": "POST", "path": "/b", "response": { "status_code": 201, "body": { "id": 1 } } }
]Response:
{ "ok": true, "count": 2 }Register a dynamic bulk decision handler. When any request hits a path ending in /GetDecisionBulk, this handler generates per-entity decisions instead of using static mock rules.
For each entity in the request, the server extracts the emailAddress or id field from the entity chain. If the value is in permit_values, the decision is DECISION_PERMIT; otherwise it uses default_decision.
Request body:
{
"permit_values": ["admin@test.com", "user-123"],
"default_decision": "DECISION_DENY"
}| Field | Type | Default | Description |
|---|---|---|---|
permit_values |
string[] | required | Values that receive DECISION_PERMIT (matched case-insensitively) |
default_decision |
string | DECISION_DENY |
Decision for non-matching entities |
Response:
{ "ok": true }Example bulk decision request (sent to any path ending in /GetDecisionBulk):
{
"decisionRequests": [
{
"entityIdentifier": {
"entityChain": {
"entities": [{ "emailAddress": "admin@test.com" }]
}
},
"resources": [{ "ephemeralId": "res-1" }]
}
]
}Example bulk decision response:
{
"decisionResponses": [
{
"resourceDecisions": [
{ "decision": "DECISION_PERMIT", "ephemeralResourceId": "res-1" }
]
}
]
}Clear all registered mock rules, the dynamic bulk decision handler, and the request log.
Response:
{ "ok": true }Return all recorded requests (both matched and unmatched).
Response:
[
{
"timestamp": "2025-01-01T00:00:00Z",
"method": "GET",
"path": "/api/users",
"headers": { "Accept": "application/json" },
"body": null,
"matched": true
}
]Health check endpoint.
Response:
{ "status": "ok" }Any request that does not hit a /__mock/* control endpoint is treated as a mockable request:
- If the path ends in
/GetDecisionBulkand a dynamic bulk rule is registered, it is handled dynamically. - Otherwise, the server looks up a rule by
METHOD /path. Rules are matched FIFO. - If a matching rule has a
timeslimit, it decrements on each use and is removed when exhausted. - If no rule matches, the server returns
404with:
{ "error": "no mock registered", "method": "GET", "path": "/api/users" }All requests (matched or not) are recorded in the request log.
The default Content-Type for all responses is application/json unless overridden by the mock rule's headers.