A client times out after asking an API to create a note. The server may have rejected the request, committed it, or still be processing it. The timeout alone cannot tell the client which occurred. This workshop makes that ambiguity visible: a simulated response disappears after the write succeeds, and the next request exposes whether the contract protects the operation.
Prerequisites: reading JSON, HTTP request and response basics, and a local Node installation for the optional download. The exercise takes roughly 25 minutes. Its deliverable is an exported request trace and a short decision record explaining the retry rules.
Write the acceptance criteria first
Open the API contract workbench. The route is POST /notes. Our exercise contract accepts exactly one title field, trims it and limits it to 80 UTF-16 code units. It uses 400 for malformed JSON, 422 for field errors, 401 for an absent identity and 403 when the identity lacks permission. These are explicit choices for this model, not a universal API schema.
A repeated key with the same normalized title must return the original id without another write. The same key with a changed title must return 409. A failed validation must not reserve a key. Authorization must be checked again before returning a cached response.
Find the bug before loading the solution
- Run the deliberately broken starter suite. Pick one failure and read its request sequence.
- In the manual session, send a valid title with key
request-1and select lost response delivery. Record the write count even though the client has no successful response. - Retry with normal delivery and the same key. Then change the title while retaining the key.
- Change the caller to permission revoked and repeat the original request. A previously permitted operation must not bypass the current authorization decision.
- Repair validation, fingerprint comparison and authorization ordering one at a time. Rerun the entire suite after each change.
The solved configuration should pass all fourteen expectations. A passing suite means the fixed exercise contract holds for its fixtures. It does not establish that a production service handles concurrent requests or survives a crash.
Continue outside the browser
Download the Node exercise from the workbench and extract it into a new directory. Run node --test contract.test.mjs. The starter intentionally fails. Edit config.json using your diagnosis, then rerun. Run node exercise.mjs to produce the run record. The kit has no package installation step or network dependency.
The production questions this model leaves open
Draw a timeline with two simultaneous requests using the same key. Both can read an empty cache before either writes. A separate cache lookup followed by an insert is therefore not an atomic deduplication mechanism. Your persistence design must coordinate the key claim, domain write and replay result, and define recovery for every interruption point.
Scope keys by authenticated principal and operation. Define expiration, maximum key size and storage budgets. Decide what a retry after expiration means. Define exactly which request fields form a fingerprint, including normalization and schema version. The workbench compares one normalized title and one identity; it deliberately does not solve those broader problems.
Finish with three notes: the invariant you protect, an untested failure mode and the integration test you would add next. A strong next test submits two concurrent requests against a real transactional store and proves there is exactly one durable domain write, then repeats the check after a controlled worker interruption.
HTTP distinguishes idempotent method semantics from an application's added retry contract. Do not assume POST is safely repeatable merely because a client has a retry loop. Read RFC 9110's idempotency section alongside the explicit rules of the API you actually use.