precondition
Enforces that unsafe requests include a conditional header (If-Match or
If-Unmodified-Since) before proceeding. Prevents lost-update problems
by requiring clients to prove they have a current representation.
Pipeline stage: Negotiation (Fast Fail)
Import
Section titled “Import”import {precondition} from '@centralping/ergo';Options
Section titled “Options”| Option | Type | Default | Description |
|---|---|---|---|
methods |
string[] | Set<string> |
— | Methods to enforce on; omit to enforce unconditionally |
Return Value
Section titled “Return Value”Returns undefined on success — the middleware is a gate.
Error Responses
Section titled “Error Responses”| Status | Condition |
|---|---|
| 428 Precondition Required | Neither If-Match nor If-Unmodified-Since header is present |
Common Pattern — Safe Write Operations
Section titled “Common Pattern — Safe Write Operations”Use preconditionRequired with the default etag: true behavior to
enforce optimistic concurrency on write operations:
import {compose, precondition} from '@centralping/ergo';
// Enforce on all methodsconst pipeline = compose( precondition(),);
// Enforce only on PUT and PATCHconst pipeline = compose( precondition({methods: ['PUT', 'PATCH']}),);router.put('/users/:id', { preconditionRequired: true, // etag: true is the send() default — no explicit config needed execute: async (req, res, acc) => { const user = await db.findAndUpdate( acc.route.params.id, acc.body.parsed, ); return {response: {body: user}}; },});// PUT without If-Match → 428 Precondition Required// PUT with stale If-Match → 412 Precondition Failed// PUT with current If-Match → 200 OKOpting Out
Section titled “Opting Out”ETag generation without enforcement — clients CAN send conditional headers but are not required to:
import {compose, precondition} from '@centralping/ergo';
// Simply omit precondition() from the pipeline.// etag: true remains the send() default — ETags are generated,// If-Match is evaluated when present, but not enforced.const pipeline = compose( (req, res, acc) => ({ response: {body: {settings: acc.settings}}, }),);router.put('/settings/:id', { preconditionRequired: false, // etag: true remains the default — ETags are generated, // If-Match is evaluated when present execute: updateSettings,});RFC References
Section titled “RFC References”Related Recipes
Section titled “Related Recipes”- Testing Patterns — ETag round-trip and 304 conditional request testing patterns
API Reference
Section titled “API Reference”See the auto-generated precondition API docs.