Skip to content

@nestarc/idempotency ​

Classes ​

IdempotencyInterceptor ​

Defined in: src/idempotency.interceptor.ts:100

The core idempotency interceptor.

Reads @Idempotent() metadata off the handler, extracts the configured idempotency header, computes a request body fingerprint, and dispatches the storage state machine: replay COMPLETED, conflict on PROCESSING, mismatch on differing fingerprint, otherwise lock + delegate + capture response under token-based compare-and-set.

Implements the IETF draft httpapi-idempotency-key-header-07 semantics for 400 / 409 / 422 responses.

Implements ​

  • NestInterceptor

Constructors ​

Constructor ​
ts
new IdempotencyInterceptor(
   reflector,
   storage,
   moduleOptions): IdempotencyInterceptor;

Defined in: src/idempotency.interceptor.ts:103

Parameters ​
ParameterType
reflectorReflector
storageIdempotencyStorage
moduleOptionsIdempotencyOptions
Returns ​

IdempotencyInterceptor

Methods ​

intercept() ​
ts
intercept(context, next): Observable<unknown>;

Defined in: src/idempotency.interceptor.ts:119

Method to implement a custom interceptor.

Parameters ​
ParameterTypeDescription
contextExecutionContextan ExecutionContext object providing methods to access the route handler and class about to be invoked.
nextCallHandlera reference to the CallHandler, which provides access to an Observable representing the response stream from the route handler.
Returns ​

Observable<unknown>

Implementation of ​
ts
NestInterceptor.intercept

IdempotencyModule ​

Defined in: src/idempotency.module.ts:35

NestJS dynamic module exposing the IdempotencyInterceptor and the configured IdempotencyStorage.

The module does not auto-register the interceptor as APP_INTERCEPTOR — consumers opt in via one of three patterns:

  1. App-global: providers: [{ provide: APP_INTERCEPTOR, useClass: IdempotencyInterceptor }]
  2. Controller-scoped: @UseInterceptors(IdempotencyInterceptor) on the class
  3. Method-scoped: @UseInterceptors(IdempotencyInterceptor) on the handler

The module is registered as global by default so consumers can wire any of the three patterns without re-importing it everywhere.

Constructors ​

Constructor ​
ts
new IdempotencyModule(): IdempotencyModule;
Returns ​

IdempotencyModule

Methods ​

forRoot() ​
ts
static forRoot(options): DynamicModule;

Defined in: src/idempotency.module.ts:36

Parameters ​
ParameterType
optionsIdempotencyOptions
Returns ​

DynamicModule

forRootAsync() ​
ts
static forRootAsync(options): DynamicModule;

Defined in: src/idempotency.module.ts:58

Parameters ​
ParameterType
optionsIdempotencyAsyncOptions
Returns ​

DynamicModule


MemoryStorage ​

Defined in: src/storage/memory.storage.ts:25

In-memory implementation of IdempotencyStorage.

Backed by a Map with per-entry setTimeout expirations. Suitable for tests and single-instance development. Not safe for production: state is lost on restart and not shared across processes — two replicas would each enforce idempotency independently, letting duplicates slip through.

Implements ​

Constructors ​

Constructor ​
ts
new MemoryStorage(): MemoryStorage;
Returns ​

MemoryStorage

Methods ​

complete() ​
ts
complete(
   key,
   token,
   response,
ttlSeconds): Promise<MutateResult>;

Defined in: src/storage/memory.storage.ts:66

Transitions a PROCESSING record to COMPLETED and stores the captured response, but ONLY if the stored record's token matches the caller's token. Returns 'stale' if the token does not match — meaning the original record was evicted and a newer one exists under this key. The caller's response must not overwrite the newer record.

On 'ok', implementations must refresh the TTL to ttlSeconds.

Parameters ​
ParameterType
keystring
tokenstring
responseCompleteResponse
ttlSecondsnumber
Returns ​

Promise<MutateResult>

Implementation of ​

IdempotencyStorage.complete

create() ​
ts
create(
   key,
   fingerprint,
ttlSeconds): Promise<CreateResult>;

Defined in: src/storage/memory.storage.ts:41

Atomically creates a PROCESSING record. On success, returns an opaque token that the caller MUST pass back to complete() / delete().

Parameters ​
ParameterTypeDescription
keystringthe idempotency key from the client header (already scoped by the interceptor to include endpoint identity)
fingerprintstring | undefinedSHA-256 of the request body, or undefined if fingerprinting is off
ttlSecondsnumberlifetime of the lock; the interceptor passes the resolved TTL
Returns ​

Promise<CreateResult>

Implementation of ​

IdempotencyStorage.create

delete() ​
ts
delete(key, token): Promise<MutateResult>;

Defined in: src/storage/memory.storage.ts:105

Removes a record, but ONLY if the caller's token matches. Returns 'ok' if the record was removed OR was already absent (idempotent cleanup), and 'stale' only if a DIFFERENT record (with a different token) is currently stored under this key.

Parameters ​
ParameterType
keystring
tokenstring
Returns ​

Promise<MutateResult>

Implementation of ​

IdempotencyStorage.delete

get() ​
ts
get(key): Promise<IdempotencyRecord | null>;

Defined in: src/storage/memory.storage.ts:28

Fetches a record by key. Returns null if the key does not exist or has expired.

Parameters ​
ParameterType
keystring
Returns ​

Promise<IdempotencyRecord | null>

Implementation of ​

IdempotencyStorage.get

onModuleDestroy() ​
ts
onModuleDestroy(): Promise<void>;

Defined in: src/storage/memory.storage.ts:122

Lifecycle hook: clear all pending eviction timers when the module is torn down. Prevents leaked timers from keeping the Node event loop alive in long test runs.

Returns ​

Promise<void>

Implementation of ​
ts
OnModuleDestroy.onModuleDestroy

PostgresStorage ​

Defined in: src/storage/postgres.storage.ts:72

Postgres-backed implementation of IdempotencyStorage.

Stores each record as a row in idempotency_records (override via tableName). Atomic NX is enforced by the primary-key constraint on key combined with INSERT ... ON CONFLICT DO UPDATE WHERE expires_at < now(). Token-based compare-and-set is enforced by WHERE token = $ clauses on complete() and delete(). Lazy expiration is enforced by WHERE expires_at > now() in get().

For active cleanup of expired rows see PostgresSweepService.

Implements ​

Constructors ​

Constructor ​
ts
new PostgresStorage(options): PostgresStorage;

Defined in: src/storage/postgres.storage.ts:78

Parameters ​
ParameterType
optionsPostgresStorageOptions
Returns ​

PostgresStorage

Methods ​

close() ​
ts
close(): Promise<void>;

Defined in: src/storage/postgres.storage.ts:233

Returns ​

Promise<void>

complete() ​
ts
complete(
   key,
   token,
   response,
ttlSeconds): Promise<MutateResult>;

Defined in: src/storage/postgres.storage.ts:172

Transitions a PROCESSING record to COMPLETED and stores the captured response, but ONLY if the stored record's token matches the caller's token. Returns 'stale' if the token does not match — meaning the original record was evicted and a newer one exists under this key. The caller's response must not overwrite the newer record.

On 'ok', implementations must refresh the TTL to ttlSeconds.

Parameters ​
ParameterType
keystring
tokenstring
responseCompleteResponse
ttlSecondsnumber
Returns ​

Promise<MutateResult>

Implementation of ​

IdempotencyStorage.complete

create() ​
ts
create(
   key,
   fingerprint,
ttlSeconds): Promise<CreateResult>;

Defined in: src/storage/postgres.storage.ts:143

Atomically creates a PROCESSING record. On success, returns an opaque token that the caller MUST pass back to complete() / delete().

Parameters ​
ParameterTypeDescription
keystringthe idempotency key from the client header (already scoped by the interceptor to include endpoint identity)
fingerprintstring | undefinedSHA-256 of the request body, or undefined if fingerprinting is off
ttlSecondsnumberlifetime of the lock; the interceptor passes the resolved TTL
Returns ​

Promise<CreateResult>

Implementation of ​

IdempotencyStorage.create

createSchema() ​
ts
static createSchema(pool, tableName?): Promise<void>;

Defined in: src/storage/postgres.storage.ts:248

Idempotently creates the records table and supporting index. Safe to call multiple times. Used by autoCreateSchema=true and available as a public helper for code-driven migrations.

Parameters ​
ParameterTypeDefault value
poolPoolundefined
tableNamestringDEFAULT_TABLE_NAME
Returns ​

Promise<void>

delete() ​
ts
delete(key, token): Promise<MutateResult>;

Defined in: src/storage/postgres.storage.ts:209

Removes a record, but ONLY if the caller's token matches. Returns 'ok' if the record was removed OR was already absent (idempotent cleanup), and 'stale' only if a DIFFERENT record (with a different token) is currently stored under this key.

Parameters ​
ParameterType
keystring
tokenstring
Returns ​

Promise<MutateResult>

Implementation of ​

IdempotencyStorage.delete

get() ​
ts
get(key): Promise<IdempotencyRecord | null>;

Defined in: src/storage/postgres.storage.ts:110

Fetches a record by key. Returns null if the key does not exist or has expired.

Parameters ​
ParameterType
keystring
Returns ​

Promise<IdempotencyRecord | null>

Implementation of ​

IdempotencyStorage.get

onModuleDestroy() ​
ts
onModuleDestroy(): Promise<void>;

Defined in: src/storage/postgres.storage.ts:239

Returns ​

Promise<void>

Implementation of ​
ts
OnModuleDestroy.onModuleDestroy

onModuleInit() ​
ts
onModuleInit(): Promise<void>;

Defined in: src/storage/postgres.storage.ts:104

Returns ​

Promise<void>


PostgresSweepService ​

Defined in: src/services/postgres-sweep.service.ts:34

Optional service that periodically deletes expired idempotency records.

Lazy expiration in PostgresStorage.get already guarantees correctness; this service exists only to keep disk usage and dead tuples bounded in long-running deployments.

Multi-instance safety: each sweep wraps DELETE in pg_try_advisory_lock(hashtext('idempotency-sweep')). Concurrent replicas will see a lock contention and skip — no DELETE storms.

Implements ​

  • OnModuleInit
  • OnModuleDestroy

Constructors ​

Constructor ​
ts
new PostgresSweepService(storage, options?): PostgresSweepService;

Defined in: src/services/postgres-sweep.service.ts:38

Parameters ​
ParameterType
storagePostgresStorage
optionsSweepOptions
Returns ​

PostgresSweepService

Methods ​

onModuleDestroy() ​
ts
onModuleDestroy(): Promise<void>;

Defined in: src/services/postgres-sweep.service.ts:56

Returns ​

Promise<void>

Implementation of ​
ts
OnModuleDestroy.onModuleDestroy

onModuleInit() ​
ts
onModuleInit(): Promise<void>;

Defined in: src/services/postgres-sweep.service.ts:45

Returns ​

Promise<void>

Implementation of ​
ts
OnModuleInit.onModuleInit

sweep() ​
ts
sweep(): Promise<{
  deleted: number;
}>;

Defined in: src/services/postgres-sweep.service.ts:64

Runs one sweep cycle. Returns the number of rows deleted (0 if another replica holds the advisory lock for this cycle).

Returns ​

Promise<{ deleted: number; }>


RedisStorage ​

Defined in: src/storage/redis.storage.ts:76

Redis-backed implementation of IdempotencyStorage.

Stores each record as a Redis Hash under ${keyPrefix}${key} with two fields: token (opaque UUID owned by the creating caller) and payload (JSON-serialized SerializedPayload). All mutations go through Lua scripts registered with defineCommand so the compare-and-set logic runs atomically on the Redis server — closing the race window that a GET-then-SET pattern would leave open.

Implements ​

Constructors ​

Constructor ​
ts
new RedisStorage(options): RedisStorage;

Defined in: src/storage/redis.storage.ts:81

Parameters ​
ParameterType
optionsRedisStorageOptions
Returns ​

RedisStorage

Methods ​

close() ​
ts
close(): Promise<void>;

Defined in: src/storage/redis.storage.ts:202

Closes the internally-managed Redis client. No-op if the client was supplied by the consumer (they own its lifecycle).

Normally called automatically via onModuleDestroy() during Nest's shutdown. Exposed publicly so non-Nest consumers (or manual teardown in tests) can trigger the cleanup without going through the module lifecycle.

Returns ​

Promise<void>

complete() ​
ts
complete(
   key,
   token,
   response,
ttlSeconds): Promise<MutateResult>;

Defined in: src/storage/redis.storage.ts:156

Transitions a PROCESSING record to COMPLETED and stores the captured response, but ONLY if the stored record's token matches the caller's token. Returns 'stale' if the token does not match — meaning the original record was evicted and a newer one exists under this key. The caller's response must not overwrite the newer record.

On 'ok', implementations must refresh the TTL to ttlSeconds.

Parameters ​
ParameterType
keystring
tokenstring
responseCompleteResponse
ttlSecondsnumber
Returns ​

Promise<MutateResult>

Implementation of ​

IdempotencyStorage.complete

create() ​
ts
create(
   key,
   fingerprint,
ttlSeconds): Promise<CreateResult>;

Defined in: src/storage/redis.storage.ts:131

Atomically creates a PROCESSING record. On success, returns an opaque token that the caller MUST pass back to complete() / delete().

Parameters ​
ParameterTypeDescription
keystringthe idempotency key from the client header (already scoped by the interceptor to include endpoint identity)
fingerprintstring | undefinedSHA-256 of the request body, or undefined if fingerprinting is off
ttlSecondsnumberlifetime of the lock; the interceptor passes the resolved TTL
Returns ​

Promise<CreateResult>

Implementation of ​

IdempotencyStorage.create

delete() ​
ts
delete(key, token): Promise<MutateResult>;

Defined in: src/storage/redis.storage.ts:188

Removes a record, but ONLY if the caller's token matches. Returns 'ok' if the record was removed OR was already absent (idempotent cleanup), and 'stale' only if a DIFFERENT record (with a different token) is currently stored under this key.

Parameters ​
ParameterType
keystring
tokenstring
Returns ​

Promise<MutateResult>

Implementation of ​

IdempotencyStorage.delete

get() ​
ts
get(key): Promise<IdempotencyRecord | null>;

Defined in: src/storage/redis.storage.ts:112

Fetches a record by key. Returns null if the key does not exist or has expired.

Parameters ​
ParameterType
keystring
Returns ​

Promise<IdempotencyRecord | null>

Implementation of ​

IdempotencyStorage.get

onModuleDestroy() ​
ts
onModuleDestroy(): Promise<void>;

Defined in: src/storage/redis.storage.ts:217

Nest lifecycle hook — fires automatically when the host module is destroyed (e.g. during app.close()). Delegates to close so consumers who pass only connection options (letting this class own the client) get graceful teardown without manual bookkeeping.

If the consumer supplied their own client, this hook is a no-op: they remain responsible for closing what they created.

Returns ​

Promise<void>

Implementation of ​
ts
OnModuleDestroy.onModuleDestroy

Interfaces ​

CompleteResponse ​

Defined in: src/interfaces/idempotency-storage.interface.ts:6

The response payload captured by the interceptor and persisted by storage.

Properties ​

body? ​
ts
optional body?: string;

Defined in: src/interfaces/idempotency-storage.interface.ts:11

JSON-serialized response body, or undefined for empty bodies (e.g. 204).

headers? ​
ts
optional headers?: Record<string, string>;

Defined in: src/interfaces/idempotency-storage.interface.ts:14

Lowercase HTTP response headers captured for replay.

statusCode ​
ts
statusCode: number;

Defined in: src/interfaces/idempotency-storage.interface.ts:8

HTTP status code emitted by the original handler.


CreateResult ​

Defined in: src/interfaces/idempotency-storage.interface.ts:28

Return shape of IdempotencyStorage.create.

acquired === true means this caller successfully created a new PROCESSING record and was given an opaque token that uniquely identifies that record. The caller MUST pass this token back to complete() / delete() so the storage can verify it still owns the record before mutating it.

acquired === false means a record already existed (NX semantics). No token is issued in this case.

Properties ​

acquired ​
ts
acquired: boolean;

Defined in: src/interfaces/idempotency-storage.interface.ts:29

token? ​
ts
optional token?: string;

Defined in: src/interfaces/idempotency-storage.interface.ts:30


IdempotencyAsyncOptions ​

Defined in: src/interfaces/idempotency-options.interface.ts:184

Async configuration passed to IdempotencyModule.forRootAsync. Mirrors the standard NestJS async-module pattern (useFactory / useClass / useExisting).

Extends ​

  • Pick<ModuleMetadata, "imports">

Properties ​

imports? ​
ts
optional imports?: (
  | DynamicModule
  | Type<any>
  | Promise<DynamicModule>
  | ForwardReference<any>)[];

Defined in: node_modules/@nestjs/common/interfaces/modules/module-metadata.interface.d.ts:18

Optional list of imported modules that export the providers which are required in this module.

Inherited from ​
ts
Pick.imports

inject? ​
ts
optional inject?: any[];

Defined in: src/interfaces/idempotency-options.interface.ts:190

isGlobal? ​
ts
optional isGlobal?: boolean;

Defined in: src/interfaces/idempotency-options.interface.ts:191

useClass? ​
ts
optional useClass?: Type<IdempotencyOptionsFactory>;

Defined in: src/interfaces/idempotency-options.interface.ts:186

useExisting? ​
ts
optional useExisting?: Type<IdempotencyOptionsFactory>;

Defined in: src/interfaces/idempotency-options.interface.ts:185

useFactory? ​
ts
optional useFactory?: (...args) =>
  | IdempotencyOptions
| Promise<IdempotencyOptions>;

Defined in: src/interfaces/idempotency-options.interface.ts:187

Parameters ​
ParameterType
...argsany[]
Returns ​

| IdempotencyOptions | Promise<IdempotencyOptions>


IdempotencyOptions ​

Defined in: src/interfaces/idempotency-options.interface.ts:79

Module-level configuration passed to IdempotencyModule.forRoot.

Properties ​

fingerprint? ​
ts
optional fingerprint?: boolean | IdempotencyFingerprintResolver;

Defined in: src/interfaces/idempotency-options.interface.ts:136

When true, the interceptor computes a SHA-256 fingerprint of the request body and verifies it on subsequent requests. Pass a resolver function to provide an application-specific semantic fingerprint. A mismatch produces HTTP 422.

Default ​
ts
true

headerName? ​
ts
optional headerName?: string;

Defined in: src/interfaces/idempotency-options.interface.ts:114

The HTTP header name carrying the idempotency key. Override only if you need to deviate from the IETF draft default.

Default ​
ts
'Idempotency-Key'

isGlobal? ​
ts
optional isGlobal?: boolean;

Defined in: src/interfaces/idempotency-options.interface.ts:168

When true, the module is registered as a global module (no need to import it into every consumer module).

Default ​
ts
true

keyResolver? ​
ts
optional keyResolver?: IdempotencyKeyResolver;

Defined in: src/interfaces/idempotency-options.interface.ts:120

Optional application-level idempotency key resolver. When configured, its return value is used instead of reading the configured header.

maxKeyLength? ​
ts
optional maxKeyLength?: number;

Defined in: src/interfaces/idempotency-options.interface.ts:127

Maximum accepted idempotency key length, in characters.

Default ​
ts
255

observability? ​
ts
optional observability?: IdempotencyObservabilityOptions;

Defined in: src/interfaces/idempotency-options.interface.ts:160

Optional operational hooks and client-visible status headers.

processingTtl? ​
ts
optional processingTtl?: number;

Defined in: src/interfaces/idempotency-options.interface.ts:106

Optional time-to-live for in-flight PROCESSING records, in seconds. When omitted, ttl is used for both processing locks and completed replay records. Per-handler @Idempotent({ processingTtl }) overrides this.

Configure this only when you want stuck in-flight records to expire sooner than completed replay records. Values shorter than the endpoint's real processing time can allow duplicate execution.

replayHeaders? ​
ts
optional replayHeaders?: ReplayHeadersOption;

Defined in: src/interfaces/idempotency-options.interface.ts:155

Controls which response headers are captured and replayed.

true or undefined uses the conservative default allowlist. false disables header replay. A string array uses an explicit allowlist, still filtered through the unsafe header denylist.

Default ​
ts
true

scope? ​
ts
optional scope?: IdempotencyScope;

Defined in: src/interfaces/idempotency-options.interface.ts:143

How storage keys are namespaced. See IdempotencyScope.

Default ​
ts
'endpoint'

storage ​
ts
storage: IdempotencyStorage;

Defined in: src/interfaces/idempotency-options.interface.ts:85

The storage adapter instance to use. Construct it yourself (e.g. new MemoryStorage() or new RedisStorage({ host, port })) for full type-safe control over adapter wiring.

ttl? ​
ts
optional ttl?: number;

Defined in: src/interfaces/idempotency-options.interface.ts:95

Default time-to-live for idempotency records, in seconds. Per-handler @Idempotent({ ttl }) overrides this. Completed replay records use this TTL. In-flight PROCESSING records also use this TTL unless processingTtl is configured.

Default ​
ts
86400 (24 hours)

IdempotencyOptionsFactory ​

Defined in: src/interfaces/idempotency-options.interface.ts:174

Factory contract for useClass / useExisting async registration paths.

Methods ​

createIdempotencyOptions() ​
ts
createIdempotencyOptions():
  | IdempotencyOptions
| Promise<IdempotencyOptions>;

Defined in: src/interfaces/idempotency-options.interface.ts:175

Returns ​

| IdempotencyOptions | Promise<IdempotencyOptions>


IdempotencyRecord ​

Defined in: src/interfaces/idempotency-record.interface.ts:14

The persisted shape of an idempotency record across all storage adapters.

Properties ​

createdAt ​
ts
createdAt: Date;

Defined in: src/interfaces/idempotency-record.interface.ts:54

When the record was first created by IdempotencyStorage.create().

Invariant: this field is IMMUTABLE over the lifetime of a record. complete() and any other mutation MUST preserve the original value. Storage adapters that rewrite createdAt on update are non-conformant and WILL break consumers who use it for monitoring (e.g. first-seen timestamps in metrics / audit trails).

expiresAt ​
ts
expiresAt: Date;

Defined in: src/interfaces/idempotency-record.interface.ts:61

When the record will be evicted by the storage adapter. Unlike createdAt, this field IS mutated on complete() when the adapter refreshes the TTL window to the new (typically longer) value.

fingerprint? ​
ts
optional fingerprint?: string;

Defined in: src/interfaces/idempotency-record.interface.ts:31

SHA-256 of the request body, used to detect a key being reused with a different payload (which produces HTTP 422 per the IETF draft). Undefined when fingerprinting is disabled.

key ​
ts
key: string;

Defined in: src/interfaces/idempotency-record.interface.ts:16

The exact value of the Idempotency-Key header from the original request.

responseBody? ​
ts
optional responseBody?: string;

Defined in: src/interfaces/idempotency-record.interface.ts:40

JSON-serialized response body, ready to be parsed and replayed.

responseHeaders? ​
ts
optional responseHeaders?: Record<string, string>;

Defined in: src/interfaces/idempotency-record.interface.ts:43

Lowercase HTTP response headers captured for replay.

status ​
ts
status: IdempotencyStatus;

Defined in: src/interfaces/idempotency-record.interface.ts:34

Current lifecycle state.

statusCode? ​
ts
optional statusCode?: number;

Defined in: src/interfaces/idempotency-record.interface.ts:37

Captured HTTP status code of the original handler response.

token ​
ts
token: string;

Defined in: src/interfaces/idempotency-record.interface.ts:24

Opaque token issued by IdempotencyStorage.create() that uniquely identifies THIS record across its lifetime. Used by complete() / delete() to compare-and-set so that a slow caller cannot clobber a newer caller's record after TTL eviction.


IdempotencyStorage ​

Defined in: src/interfaces/idempotency-storage.interface.ts:74

Pluggable storage contract for idempotency records.

Implementations must guarantee:

  1. Atomic creation (NX semantics) — two concurrent create() calls for the same key must result in exactly one acquired: true and one acquired: false.
  2. Token-based compare-and-set on complete() / delete() — a caller can only mutate a record whose stored token matches the token they received from their own create() call. This prevents the TTL-eviction race where a slow caller would otherwise clobber a newer caller's record.
  3. createdAt immutability — complete() and any other mutation MUST preserve the createdAt field of the original PROCESSING record. See IdempotencyRecord.createdAt.

Lifecycle ​

Storage adapters that hold external resources (Redis clients, DB connections, timers) SHOULD implement Nest's OnModuleDestroy hook so the resources are released when the host application shuts down. Both built-in adapters (MemoryStorage, RedisStorage) do this — a custom adapter is free to opt in the same way.

A cross-adapter contract suite that exercises every requirement of this interface lives at test/support/shared-storage-contract.ts — new adapters should be plugged into it to guarantee LSP-level uniformity.

Methods ​

complete() ​
ts
complete(
   key,
   token,
   response,
ttlSeconds): Promise<MutateResult>;

Defined in: src/interfaces/idempotency-storage.interface.ts:104

Transitions a PROCESSING record to COMPLETED and stores the captured response, but ONLY if the stored record's token matches the caller's token. Returns 'stale' if the token does not match — meaning the original record was evicted and a newer one exists under this key. The caller's response must not overwrite the newer record.

On 'ok', implementations must refresh the TTL to ttlSeconds.

Parameters ​
ParameterType
keystring
tokenstring
responseCompleteResponse
ttlSecondsnumber
Returns ​

Promise<MutateResult>

create() ​
ts
create(
   key,
   fingerprint,
ttlSeconds): Promise<CreateResult>;

Defined in: src/interfaces/idempotency-storage.interface.ts:89

Atomically creates a PROCESSING record. On success, returns an opaque token that the caller MUST pass back to complete() / delete().

Parameters ​
ParameterTypeDescription
keystringthe idempotency key from the client header (already scoped by the interceptor to include endpoint identity)
fingerprintstring | undefinedSHA-256 of the request body, or undefined if fingerprinting is off
ttlSecondsnumberlifetime of the lock; the interceptor passes the resolved TTL
Returns ​

Promise<CreateResult>

delete() ​
ts
delete(key, token): Promise<MutateResult>;

Defined in: src/interfaces/idempotency-storage.interface.ts:117

Removes a record, but ONLY if the caller's token matches. Returns 'ok' if the record was removed OR was already absent (idempotent cleanup), and 'stale' only if a DIFFERENT record (with a different token) is currently stored under this key.

Parameters ​
ParameterType
keystring
tokenstring
Returns ​

Promise<MutateResult>

get() ​
ts
get(key): Promise<IdempotencyRecord | null>;

Defined in: src/interfaces/idempotency-storage.interface.ts:78

Fetches a record by key. Returns null if the key does not exist or has expired.

Parameters ​
ParameterType
keystring
Returns ​

Promise<IdempotencyRecord | null>


IdempotentMetadata ​

Defined in: src/interfaces/idempotency-options.interface.ts:238

The metadata shape persisted via SetMetadata by the Idempotent decorator. The enabled: true flag lets the interceptor distinguish "decorator applied with no overrides" from "no decorator at all".

Extends ​

Properties ​

enabled ​
ts
enabled: true;

Defined in: src/interfaces/idempotency-options.interface.ts:239

fingerprint? ​
ts
optional fingerprint?: boolean | IdempotencyFingerprintResolver;

Defined in: src/interfaces/idempotency-options.interface.ts:230

Override the module-level fingerprint setting for this handler.

Inherited from ​

IdempotentOptions.fingerprint

keyResolver? ​
ts
optional keyResolver?: IdempotencyKeyResolver;

Defined in: src/interfaces/idempotency-options.interface.ts:220

Override the module-level key resolver for this handler.

Inherited from ​

IdempotentOptions.keyResolver

maxKeyLength? ​
ts
optional maxKeyLength?: number;

Defined in: src/interfaces/idempotency-options.interface.ts:225

Override the module-level maximum key length for this handler.

Inherited from ​

IdempotentOptions.maxKeyLength

processingTtl? ​
ts
optional processingTtl?: number;

Defined in: src/interfaces/idempotency-options.interface.ts:215

Override the module-level processing TTL for this handler (in seconds).

Inherited from ​

IdempotentOptions.processingTtl

required? ​
ts
optional required?: boolean;

Defined in: src/interfaces/idempotency-options.interface.ts:205

When true, the Idempotency-Key header is mandatory and a missing header produces HTTP 400. When false, requests without the header pass through normally (no idempotency check).

Default ​
ts
true
Inherited from ​

IdempotentOptions.required

ttl? ​
ts
optional ttl?: number;

Defined in: src/interfaces/idempotency-options.interface.ts:210

Override the module-level TTL for this handler (in seconds).

Inherited from ​

IdempotentOptions.ttl


IdempotentOptions ​

Defined in: src/interfaces/idempotency-options.interface.ts:197

Per-handler overrides accepted by the Idempotent decorator.

Extended by ​

Properties ​

fingerprint? ​
ts
optional fingerprint?: boolean | IdempotencyFingerprintResolver;

Defined in: src/interfaces/idempotency-options.interface.ts:230

Override the module-level fingerprint setting for this handler.

keyResolver? ​
ts
optional keyResolver?: IdempotencyKeyResolver;

Defined in: src/interfaces/idempotency-options.interface.ts:220

Override the module-level key resolver for this handler.

maxKeyLength? ​
ts
optional maxKeyLength?: number;

Defined in: src/interfaces/idempotency-options.interface.ts:225

Override the module-level maximum key length for this handler.

processingTtl? ​
ts
optional processingTtl?: number;

Defined in: src/interfaces/idempotency-options.interface.ts:215

Override the module-level processing TTL for this handler (in seconds).

required? ​
ts
optional required?: boolean;

Defined in: src/interfaces/idempotency-options.interface.ts:205

When true, the Idempotency-Key header is mandatory and a missing header produces HTTP 400. When false, requests without the header pass through normally (no idempotency check).

Default ​
ts
true

ttl? ​
ts
optional ttl?: number;

Defined in: src/interfaces/idempotency-options.interface.ts:210

Override the module-level TTL for this handler (in seconds).


PostgresStorageOptions ​

Defined in: src/storage/postgres.storage.ts:21

Constructor options for PostgresStorage.

Provide either a pre-built pool (recommended — lets the consumer manage connection lifecycle) OR a connection config that the storage uses to lazily build its own pool.

Properties ​

autoCreateSchema? ​
ts
optional autoCreateSchema?: boolean;

Defined in: src/storage/postgres.storage.ts:37

If true, run CREATE TABLE IF NOT EXISTS and matching index on module init. Defaults to false. Recommended only for development.

connection? ​
ts
optional connection?: PoolConfig;

Defined in: src/storage/postgres.storage.ts:25

pg PoolConfig used to lazily construct an internal pool.

pool? ​
ts
optional pool?: Pool;

Defined in: src/storage/postgres.storage.ts:23

A pre-built pg Pool. Wins over connection if both are supplied.

poolFactory? ​
ts
optional poolFactory?: (connection) => Pool;

Defined in: src/storage/postgres.storage.ts:27

Test-only seam: custom factory used in place of new Pool(connection).

Parameters ​
ParameterType
connectionPoolConfig
Returns ​

Pool

tableName? ​
ts
optional tableName?: string;

Defined in: src/storage/postgres.storage.ts:32

Table name used for idempotency records.

Default ​
ts
'idempotency_records'

RedisStorageOptions ​

Defined in: src/storage/redis.storage.ts:20

Constructor options for RedisStorage.

Provide either a pre-built client (recommended — lets the consumer manage connection lifecycle) OR a connection options object that the storage uses to lazily build its own client.

Properties ​

client? ​
ts
optional client?: Redis;

Defined in: src/storage/redis.storage.ts:22

A pre-built ioredis client. Wins over connection if both are supplied.

clientFactory? ​
ts
optional clientFactory?: (connection) => Redis;

Defined in: src/storage/redis.storage.ts:26

Test-only seam: custom factory used in place of new Redis(connection).

Parameters ​
ParameterType
connectionRedisOptions
Returns ​

Redis

connection? ​
ts
optional connection?: RedisOptions;

Defined in: src/storage/redis.storage.ts:24

ioredis connection options used to lazily construct an internal client.

keyPrefix? ​
ts
optional keyPrefix?: string;

Defined in: src/storage/redis.storage.ts:31

Prefix prepended to every idempotency key in Redis.

Default ​
ts
'idempotency:'

SweepOptions ​

Defined in: src/services/postgres-sweep.service.ts:13

Properties ​

enabled ​
ts
enabled: boolean;

Defined in: src/services/postgres-sweep.service.ts:15

When false, the service is wired up but never schedules a sweep.

intervalMs? ​
ts
optional intervalMs?: number;

Defined in: src/services/postgres-sweep.service.ts:17

Sweep cadence. Defaults to 60_000 (1 minute).

Type Aliases ​

IdempotencyScope ​

ts
type IdempotencyScope = "endpoint" | "global" | ((context) => string);

Defined in: src/interfaces/idempotency-options.interface.ts:21

How the interceptor derives the storage-key namespace from the request.

  • 'endpoint' (default) — scope by actual HTTP method + request path when available, falling back to Nest route metadata and then controller class + handler method name. Two different endpoints using the SAME Idempotency-Key value will NOT collide. Matches the IETF draft recommendation that the key be unique per (key, request URI) tuple.

  • 'global' — legacy behavior: use the raw header value as the storage key with no namespace. Safe only if clients guarantee globally-unique keys across all endpoints (e.g. fresh UUIDs per request).

  • A function (ctx) => string — fully custom scoping. Useful in multi-tenant systems where the scope should include the tenant ID. The returned string will be combined with the raw header value.


IdempotencyStatus ​

ts
type IdempotencyStatus = "PROCESSING" | "COMPLETED";

Defined in: src/interfaces/idempotency-record.interface.ts:9

Lifecycle state of an idempotency record.

  • PROCESSING: a request with this key is currently being handled. A duplicate request arriving in this state should receive HTTP 409 Conflict.
  • COMPLETED: the request finished and its response is cached. A duplicate request with the same fingerprint should be replayed from the stored response.

MutateResult ​

ts
type MutateResult = "ok" | "stale";

Defined in: src/interfaces/idempotency-storage.interface.ts:45

Return shape of IdempotencyStorage.complete and IdempotencyStorage.delete.

  • 'ok': the operation succeeded — the caller's token matched the stored record (or, for delete, the record was already absent).
  • 'stale': the caller's token does NOT match the record currently stored under this key. This happens when the original PROCESSING record was evicted by TTL and a newer caller has since created a fresh record. The original caller MUST NOT touch the newer record; storage silently refused the write.

ReplayHeadersOption ​

ts
type ReplayHeadersOption = boolean | string[];

Defined in: src/interfaces/idempotency-options.interface.ts:26

Variables ​

DEFAULT_HEADER_NAME ​

ts
const DEFAULT_HEADER_NAME: "Idempotency-Key" = 'Idempotency-Key';

Defined in: src/idempotency.constants.ts:29

Default HTTP header name carrying the idempotency key. Matches the IETF draft httpapi-idempotency-key-header-07.


DEFAULT_TTL_SECONDS ​

ts
const DEFAULT_TTL_SECONDS: 86400 = 86_400;

Defined in: src/idempotency.constants.ts:34

Default time-to-live for idempotency records, in seconds (24 hours).


IDEMPOTENCY_OPTIONS ​

ts
const IDEMPOTENCY_OPTIONS: typeof IDEMPOTENCY_OPTIONS;

Defined in: src/idempotency.constants.ts:4

Injection token for the resolved IdempotencyOptions instance.


IDEMPOTENCY_STORAGE ​

ts
const IDEMPOTENCY_STORAGE: typeof IDEMPOTENCY_STORAGE;

Defined in: src/idempotency.constants.ts:9

Injection token for the IdempotencyStorage instance the interceptor uses.


IDEMPOTENCY_SWEEP_OPTIONS ​

ts
const IDEMPOTENCY_SWEEP_OPTIONS: typeof IDEMPOTENCY_SWEEP_OPTIONS;

Defined in: src/idempotency.constants.ts:15

Injection token for SweepOptions. Optional — when not provided the PostgresSweepService runs in disabled mode (no scheduled cleanup).


IDEMPOTENT_METADATA_KEY ​

ts
const IDEMPOTENT_METADATA_KEY: "nestarc:idempotent" = 'nestarc:idempotent';

Defined in: src/idempotency.constants.ts:23

Reflector metadata key carrying the per-handler IdempotentMetadata.

Stored as a plain string (not Symbol) for maximum compatibility with Nest's Reflector.get and Reflect.getMetadata.

Functions ​

Idempotent() ​

ts
function Idempotent(options?): MethodDecorator;

Defined in: src/idempotency.decorator.ts:29

Marks a NestJS controller handler as idempotent.

The IdempotencyInterceptor reads this metadata to decide whether to apply duplicate-request protection: extracting the Idempotency-Key header, computing a request fingerprint, and replaying cached responses for repeats.

Parameters ​

ParameterType
options?IdempotentOptions

Returns ​

MethodDecorator

Examples ​

Basic usage — header is required, body fingerprinted, default TTL.

ts
@Post()
@Idempotent()
createPayment(@Body() dto: CreatePaymentDto) { ... }

Per-handler overrides.

ts
@Post('refunds')
@Idempotent({ ttl: 3600, fingerprint: false })
createRefund(@Body() dto: CreateRefundDto) { ... }

Released under the MIT License.