# Expiration Policy (/sdks/nodejs/how-to/queues/expiration-policy)



## Overview [#overview]

An **expiration policy** puts a hard time limit on how long a queue message may sit unconsumed. It solves a different problem than a dead-letter policy — this isn't about messages that fail processing, it's about messages that go *stale*: a price quote, a one-time code, a cache-invalidation signal, where late delivery is actively wrong, not just delayed. Instead of every consumer re-checking timestamps itself, the deadline lives on the message and the broker enforces it.

At the API level, `policy: { expirationSeconds: 30 }` attaches a per-message TTL when you build the message, and the clock starts the moment the broker accepts it via `sendQueueMessage`, not when a consumer picks it up. Let the TTL elapse unconsumed and the broker silently removes it — a later poll just comes back empty, no error, no trace.

**Gotchas:** expiration is silent — no DLQ routing, no event, just a message that vanishes — so pair it with monitoring if you need visibility into how much work is being dropped. The timer starts at send time, not when a consumer picks up the work, so a message can expire mid-backlog even while a consumer is actively polling. And setting the TTL too short for your real consumer lag just turns ordinary slowness into silent data loss.

## Prerequisites [#prerequisites]

* KubeMQ server running on `localhost:50000`
* Node.js SDK installed (`npm install kubemq-js`)

## Code [#code]

```typescript title="expiration-policy.ts"
import { KubeMQClient, createQueueMessage } from 'kubemq-js';

async function main() {
  const client = await KubeMQClient.create({
    address: 'localhost:50000',
    clientId: 'js-queues-stream-expiration-policy-client',
  });

  try {
    const result = await client.sendQueueMessage(
      createQueueMessage({
        channel: 'js-queues-stream.expiration-policy',
        body: 'this message expires in 30 seconds',
        policy: { expirationSeconds: 30 },
      }),
    );

    console.log('Sent message ID:', result.messageId);
    const expiresAt =
      result.expirationAt instanceof Date && !isNaN(result.expirationAt.getTime())
        ? result.expirationAt.toISOString()
        : 'N/A';
    console.log('Expires at:', expiresAt);
  } finally {
    await client.close();
  }
}

main().catch(console.error);

```

## How It Works [#how-it-works]

* `policy: { expirationSeconds: 30 }` sets a TTL on the message; if no consumer receives it within 30 seconds, the server automatically discards it.
* `result.expirationAt` is a `Date` confirming the expiry deadline — the `instanceof Date && !isNaN(...)` guard handles older server versions that may not set it.
* Unlike dead-letter routing, expired messages are simply discarded — there is no DLQ involved unless you also set `maxReceiveQueue`.
* This pattern is useful for time-sensitive work (e.g., alerts, cache invalidation) where processing a stale message is worse than missing it.

## Related [#related]

* [Pattern overview](/learn/queues/getting-started)
* [Node.js SDK Reference](/sdks/nodejs/reference)
* [Send & Receive](/sdks/nodejs/tutorials/send-receive)
* [Ack All](/sdks/nodejs/how-to/queues/ack-all)
