Install & Compatibility
Where this runs
tested against v? · npm install
Install × environment matrix
Each cell = how many times install + import succeeded across repeated harness runs. Partial = flaky.
glibc = Debian/Ubuntu slim · musl = Alpine Linux
muslnode 18–226 runs
build_error
glibcnode 18–226 runs
build_error
Code
Verified usage
Verified import paths — ran on the pinned version, not inferred.
idempotency
✓ import { idempotency } from 'express-idempotency';
✗ const idempotency = require('express-idempotency'); // This tries to call the module directly, not the named export
The primary export is a named middleware function. While CommonJS `require` can also be used with destructuring (e.g., `const { idempotency } = require('express-idempotency');`), ESM `import` statements are idiomatic for modern Node.js and TypeScript environments, especially since v2.0.0.
IIdempotencyDataAdapter
✓ import type { IIdempotencyDataAdapter } from 'express-idempotency';
✗ import { IIdempotencyDataAdapter } from 'express-idempotency'; // Imports as a value instead of a type
This is a TypeScript interface for defining custom data storage. Use `import type` to clearly indicate it's a type-only import and prevent potential bundling issues or runtime overhead.
getSharedIdempotencyService
✓ const idempotencyService = getSharedIdempotencyService();
✗ import { getSharedIdempotencyService } from 'express-idempotency'; // This function is not directly exported as a module member
This helper function retrieves the singleton `IdempotencyService` instance, which is internally managed by the middleware after initialization. It is typically available in the application context after `idempotency()` middleware has been applied, rather than being a direct named module export.
This quickstart demonstrates how to install `express-idempotency`, initialize it with a basic in-memory data adapter, and integrate it into an Express route. It highlights how to use `isHit(req)` to prevent re-processing and `reportError(req)` for failed operations, ensuring idempotent behavior for a POST request.
import express from 'express';
import { idempotency, IIdempotencyDataAdapter } from 'express-idempotency';
import { v4 as uuidv4 } from 'uuid';
// IMPORTANT: For production, replace this with a persistent storage solution (Redis, MongoDB, etc.)
// The default in-memory adapter is not suitable for production environments.
class InMemoryDataAdapter implements IIdempotencyDataAdapter {
private store = new Map<string, { request: any; response: any; status: string }>();
async get(idempotencyKey: string): Promise<{ request: any; response: any; status: string } | null> {
return this.store.get(idempotencyKey) || null;
}
async set(idempotencyKey: string, request: any, response: any, status: string): Promise<void> {
this.store.set(idempotencyKey, { request, response, status });
}
async remove(idempotencyKey: string): Promise<void> {
this.store.delete(idempotencyKey);
}
}
const app = express();
const port = 3000;
// Middleware to parse JSON bodies
app.use(express.json());
// Initialize the idempotency middleware. Always provide a production-ready data adapter.
app.use(
idempotency({
dataAdapter: new InMemoryDataAdapter(), // Replace with a real data adapter for production!
// Other options like idempotencyKeyHeader, intentValidator, responseValidator can be customized here.
})
);
// Declare `getSharedIdempotencyService` for TypeScript if it's globally available.
// In a real application, consider explicitly importing a service factory if available,
// or accessing a request-scoped service if the middleware attaches it (e.g., `req.idempotencyService`).
declare function getSharedIdempotencyService(): {
isHit(req: express.Request): boolean;
reportError(req: express.Request): void;
};
app.post('/process-payment', (req, res) => {
// Retrieve the IdempotencyService instance.
const idempotencyService = getSharedIdempotencyService();
// Crucial: Check if the request is an idempotency hit. If so, prevent further processing.
if (idempotencyService.isHit(req)) {
console.log('Idempotency hit detected, a cached response should have been sent by the middleware.');
// The middleware is designed to send the cached response and then call next().
// Your route handler should return early here to avoid re-executing business logic.
return;
}
// --- Your business logic starts here (only for non-idempotent requests) ---
console.log('Processing new payment for:', req.body);
const transactionId = uuidv4();
const amount = req.body.amount;
if (amount <= 0) {
// If an error occurs during processing, report it to the middleware
// so that the idempotency state for this key can be cleared or updated.
idempotencyService.reportError(req);
return res.status(400).json({ error: 'Amount must be positive.' });
}
// Simulate an asynchronous payment processing operation
setTimeout(() => {
const responseData = {
message: `Payment for ${amount} processed successfully.`,
transactionId: transactionId,
status: 'completed',
};
res.status(200).json(responseData);
console.log('Payment processed and response sent.');
}, 1000);
});
app.listen(port, () => {
console.log(`Server listening at http://localhost:${port}`);
console.log(`
To test, send a POST request with an 'Idempotency-Key' header:`)
console.log(`curl -X POST -H "Content-Type: application/json" -H "Idempotency-Key: my-unique-key-123" -d '{"amount": 100}' http://localhost:${port}/process-payment`);
console.log(`
Repeat the curl command with the *same* 'my-unique-key-123' to observe idempotency in action (cached response).`);
console.log(`Use a *different* key for a new payment.`);
});
Errors
Common errors & fixes
TypeError: Cannot read properties of undefined (reading 'idempotency')
This typically occurs when attempting to use CommonJS `require()` without correctly destructuring the named export `idempotency`, or trying to call the `express-idempotency` module root directly as a function.
fixFor CommonJS, use `const { idempotency } = require('express-idempotency');` or `const idempotency = require('express-idempotency').idempotency;`. For ESM, use `import { idempotency } from 'express-idempotency';`. Idempotency key misuse detected (HTTP 409 Conflict)
A request was received with an `Idempotency-Key` that matches a previously processed request, but the current request's method, URL, query parameters, or body differs from the original. This indicates a potential misuse of the idempotency key.
fixClients must ensure that when retrying a request, they use the *exact same* `Idempotency-Key` and *identical* request parameters (method, URL, query, body). If the intent or payload of the request changes, a new, unique `Idempotency-Key` must be generated and used.
ReferenceError: getSharedIdempotencyService is not defined
The `getSharedIdempotencyService()` helper function, which provides access to the `IdempotencyService` instance, is not recognized in the current scope. This often happens if the `express-idempotency` middleware hasn't been properly initialized in the Express application, or if the function's availability is misunderstood.
fixEnsure that `app.use(idempotency(...))` has been called to initialize the middleware before attempting to call `getSharedIdempotencyService()`. If using TypeScript, you might need a `declare function getSharedIdempotencyService(): ...;` statement in a type definition file or at the top of your file to resolve type checking issues if it's implicitly global.
Audit
Dependencies
expressoptionalRequired as a peer dependency for any Express.js application.
http-status-codesrequiredUsed internally for HTTP status code management.
autobind-decoratorrequiredA runtime dependency to bind methods correctly, fixed as a critical dependency in v1.0.3.