API snapshots
An api target snapshots an HTTP endpoint's response — REST or GraphQL — as structured data: the status code, an allow-list of response headers, and the body. JSON bodies are parsed before snapshotting, so a change shows up as a semantic diff (~ $.body.total: 42 → 58), not a wall of re-serialized text, and numeric tolerance applies to number fields just like performance metrics.
Configure a target
json
{
"kind": "api",
"name": "checkout-total",
"url": "http://localhost:3000/api/cart/42",
"headers": { "authorization": "Bearer test-token" }
}Options:
url(required) — the endpoint to call.method— HTTP method. Defaults toGET, orPOSTwhen abodyorqueryis set.headers— request headers, sent as-is.body— a raw request body, sent verbatim (set your owncontent-typeheader).query/variables— GraphQL sugar: posts the standard{ "query": …, "variables": … }JSON envelope withcontent-type: application/json. Usequeryorbody, not both.includeHeaders— response headers to keep in the snapshot (default:["content-type"]). Headers are volatile by default — dates, request ids, rate-limit counters — so only this allow-list is stored.timeoutMs— request timeout (default: the lifecycle wait timeout).
A GraphQL target:
json
{
"kind": "api",
"name": "orders-query",
"url": "http://localhost:3000/graphql",
"query": "{ orders(last: 3) { id total } }"
}Snapshot model
json
{
"kind": "api",
"status": 200,
"headers": { "content-type": "application/json; charset=utf-8" },
"bodyType": "json",
"body": { "total": 42, "updatedAt": "<iso-timestamp>" }
}- A response that declares JSON (
content-typecontainsjson) is parsed and snapshotted structurally; anything else — and JSON that fails to parse, which is itself a regression worth seeing — is stored as text ("bodyType": "text"). - Mask rules run over every string, in headers and body alike, so dynamic values (ids, timestamps) stay stable across runs.
- The status code is part of the snapshot, not an error: an endpoint whose contract is
404can be baselined, and a drift to200fails the run like any other diff.
Comparing
dungbeetle test reports a status change first, then structural changes with their JSON paths:
~ status: 200 → 500
~ $.body.total: 42 → 58Number fields respect the config's comparison.numericTolerance, so noisy metrics can drift within bounds without failing the run.