Express middleware for oncely idempotency
npm install @oncely/expressExpress middleware for HTTP idempotency.

``bash`
npm install @oncely/core @oncely/express
`typescript
import express from 'express';
import { express as idempotent } from '@oncely/express';
const app = express();
app.use(express.json());
// Zero-config: memory storage, 24h TTL
app.post('/orders', idempotent(), async (req, res) => {
const order = await createOrder(req.body);
res.status(201).json(order);
});
`
`typescript`
app.post(
'/payments',
idempotent({
storage: redis(), // Storage adapter
ttl: '1h', // Cache duration
required: true, // Require Idempotency-Key header
methods: ['POST', 'PUT'], // HTTP methods to protect
failOpen: true, // Continue if storage fails
getKey: (req) => req.headers['x-request-id'], // Custom key extraction
getHash: (req) => hashObject(req.body), // Custom hash function
onHit: (key, response) => {}, // Cache hit callback
onMiss: (key) => {}, // Cache miss callback
onError: (key, error) => {}, // Error callback
}),
handler
);
Share configuration across routes:
`typescript
import { configure } from '@oncely/express';
import { redis } from '@oncely/redis';
const idempotent = configure({
storage: redis(),
ttl: '1h',
});
app.post('/orders', idempotent(), orderHandler);
app.post('/payments', idempotent({ required: true }), paymentHandler);
`
| Header | Direction | Description |
| -------------------- | --------- | ------------------------------------- |
| Idempotency-Key | Request | Client-provided unique key |Idempotency-Key
| | Response | Echo of the key used |Idempotency-Replay
| | Response | true when returning cached response |
All errors follow RFC 7807 Problem Details:
| Status | Type | Cause |
| ------ | -------------- | ----------------------------------- |
| 400 | key-required | Missing key when required: true |conflict
| 409 | | Request with same key in progress |mismatch
| 422 | | Same key reused with different body |
`json`
{
"type": "https://oncely.dev/errors/conflict",
"title": "Conflict",
"status": 409,
"detail": "A request with this idempotency key is already being processed"
}
`typescript
import { redis } from '@oncely/redis';
app.post('/orders', idempotent({ storage: redis() }), handler);
``
MIT