RESTful API Simulation — Patterns#

How to simulate RESTful API behavior with Simuhook stub files. Nine patterns, each with a YAML stub, the curl command, and a link to the shipped example.

CRUD ↔ HTTP Mapping#

OperationHTTP MethodTypical StatusExample Stub
List / ReadGET200 OKhealth.yaml, ping.yaml
CreatePOST201 Createduser-create.yaml
ReplacePUT200 OK
Partial updatePATCH200 OK
DeleteDELETE204 No Content
Any method(empty)200 OKmethod-match.yaml

1. Static Read Endpoint#

Return a fixed JSON response for a GET request.

id: health
method: GET
path: /api/health
response:
  status_code: 200
  body: '{"status":"ok"}'
curl http://localhost:8080/api/health
# → {"status":"ok"}

See: health.yaml


2. Create with Request Matching#

Match on specific headers and/or payload to simulate different create behaviors.

id: user-create
method: POST
path: /api/users
headers:
  X-Event: user.created
payload: '{"event":"user.created"}'
response:
  status_code: 201
  body: '{"received":true,"event":"user.created"}'
  headers:
    X-Mock: "yes"
  delay: 200ms
curl -X POST http://localhost:8080/api/users \
  -H "X-Event: user.created" \
  -d '{"event":"user.created"}'
# → {"received":true,"event":"user.created"}  (201)

See: user-create.yaml, order-placed.yaml

Variation — header-only match:

headers:
  X-Event: order.placed
response:
  status_code: 202
  delay: 500ms

3. Query-Filtered Read#

Require exact query parameters to match — simulates filtered list endpoints.

id: filtered-search
method: GET
path: /api/search
query:
  type: webhook
  version: "1.0"
response:
  status_code: 200
  body: '{"results":[]}'
curl "http://localhost:8080/api/search?type=webhook&version=1.0"
# → {"results":[]}

See: ping.yaml

Note: Query matching is all-or-nothing — every listed param must be present with the exact value. Missing or extra params cause a 404.


4. Body-Constrained Create#

Match only when the request body matches an exact payload.

id: notification-create
method: POST
path: /api/notifications
payload: '{"type":"webhook","version":"1.0"}'
response:
  status_code: 200
  body: '{"status":"notification received"}'
curl -X POST http://localhost:8080/api/notifications \
  -d '{"type":"webhook","version":"1.0"}'
# → {"status":"notification received"}

See: payload-match.yaml

Useful for testing that your client sends the exact body structure you expect. Any deviation — extra fields, reordered keys — is a 404.


5. Wildcard Method Handler#

Leave method empty to match any HTTP verb on the same path.

id: catch-all
path: /api/catch-all
response:
  status_code: 200
  body: '{"message":"matched any method"}'
curl http://localhost:8080/api/catch-all
curl -X DELETE http://localhost:8080/api/catch-all
# → {"message":"matched any method"}

See: method-match.yaml

Ideal for health checks, pre-flight handlers, or debug endpoints where you don’t care about the method.


6. Dynamic Payloads (Templates)#

Use Go templates to inject request-specific data into the response.

id: order-created
method: POST
path: /api/orders
response:
  status_code: 200
  template: true
  headers:
    X-Request-ID: "{{.RequestID}}"
    X-Timestamp: "{{.Now}}"
  body: '{"id":"{{.UUID}}","event":"order.created","timestamp":{{.Timestamp}},"method":"{{.Method}}"}'
curl -X POST http://localhost:8080/api/orders \
  -H "Content-Type: application/json" \
  -d '{"product":"widget","qty":2}'
# → {"id":"<uuid>","event":"order.created","timestamp":...,"method":"POST"}

See: order-created.yaml

Go further with faker generators ({{Name}}, {{Email}}, {{Price}}…) and JSONPath extraction in template-showcase.yaml.


7. Error / Status Simulation#

Return different responses based on weighted probability.

id: flaky-payment
method: POST
path: /api/payments
responses:
  - status_code: 200
    body: '{"status":"completed"}'
    weight: 80
  - status_code: 400
    body: '{"error":"insufficient_funds"}'
    weight: 15
  - status_code: 500
    body: '{"error":"internal_error"}'
    weight: 5
    delay: 2s
curl -X POST http://localhost:8080/api/payments   # run several times
# → different status codes roughly 80/15/5

See: flaky-webhook.yaml

Selection logic:

ConditionBehavior
Only response (no responses[])Always returns the singular response
responses[] with weightWeighted random selection
responses[] without weightRound-robin rotation (cycles in order)

Each response variant can have its own delay, echo, template, headers, and body.


8. Debug Echo#

Return the request body verbatim — inspect exactly what your client sends.

id: debug-echo
method: POST
path: /api/echo
response:
  status_code: 200
  echo: true
curl -X POST http://localhost:8080/api/echo \
  -H "Content-Type: application/json" \
  -d '{"hello":"world"}'
# → {"hello":"world"}

See: echo.yaml

echo: true takes precedence over template: true and body. The configured body is ignored when echo is active.


9. Simulated Latency#

Add artificial delays to test client timeout handling.

DurationYAMLUse Case
Instantdelay: 0s (or omit)Normal operation
Slowdelay: 500msModerate latency
Timeoutdelay: 2sClient timeout testing
Very slowdelay: 5sAggressive timeout testing
id: slow-endpoint
path: /api/slow
response:
  status_code: 200
  body: '{"ok":true}'
  delay: 2s

Delays can be set on the singular response or per-variant inside responses[].

See: user-create.yaml (200ms), order-placed.yaml (500ms), flaky-webhook.yaml (2s on the error variant)


Path Matching Notes#

Simuhook matches paths exactly (or via glob/regex — see Path Patterns). A stub for /api/users will not match /api/users/123 unless you use a wildcard:

ApproachHow
Per-resource stubsDefine separate stubs for /api/users/1, /api/users/2, etc.
Path globpath_glob: /api/users/** matches any nesting depth under /api/users.
Path regexpath_regex: ^/api/users/[0-9]+$ matches numeric IDs only.

If no endpoint matches → 404 {"error":"no matching endpoint"}.