Skip to main content
npayload is launching soon.
npayloadDocs
Use Cases

Event-driven microservices

Decouple services with pub/sub, fan-out, transactional publish, and saga orchestration

Microservices need a reliable way to communicate without tight coupling. npayload provides the messaging backbone: publish events once, deliver them to every service that needs them, with guaranteed delivery and automatic retries.

The challenge

Direct service-to-service calls create tight coupling, cascading failures, and complex retry logic. When Service A calls Service B, Service C, and Service D synchronously, a failure in any one blocks the entire chain.

How npayload solves it

Services publish events to channels. Other services subscribe independently. npayload handles fan-out, retries, and failure recovery.

Basic event publishing

// Order service publishes an event
await npayload.messages.publish({
  channel: 'orders',
  routingKey: 'order.created',
  payload: {
    event: 'order.created',
    orderId: 'ord_123',
    customerId: 'cust_456',
    items: [{ sku: 'WIDGET-001', quantity: 2, price: 29.99 }],
    total: 59.98,
  },
});

Multiple services subscribe independently

// Fulfillment service
await npayload.subscriptions.create({
  channel: 'orders',
  name: 'fulfillment',
  type: 'webhook',
  filter: { routingKey: 'order.created' },
  endpoint: { url: 'https://fulfillment.internal/webhooks/orders' },
});

// Billing service
await npayload.subscriptions.create({
  channel: 'orders',
  name: 'billing',
  type: 'webhook',
  filter: { routingKey: 'order.*' },
  endpoint: { url: 'https://billing.internal/webhooks/orders' },
});

// Analytics service
await npayload.subscriptions.create({
  channel: 'orders',
  name: 'analytics',
  type: 'webhook',
  filter: { routingKey: 'order.*' },
  endpoint: { url: 'https://analytics.internal/webhooks/orders' },
});

Each service receives events independently. If billing is down, fulfillment and analytics still receive their events. Billing receives its events when it recovers (npayload retries automatically).

Transactional publish

When a business operation spans multiple channels, use transactional publish to guarantee all events are published atomically:

await npayload.messages.publishTransactional([
  {
    channel: 'orders',
    payload: { event: 'order.created', orderId: 'ord_123' },
  },
  {
    channel: 'inventory',
    payload: { event: 'stock.reserved', sku: 'WIDGET-001', quantity: 2 },
  },
  {
    channel: 'notifications',
    payload: { event: 'order.confirmation', customerId: 'cust_456' },
  },
]);
// All three messages are published atomically, or none are

Fan-out at scale

One published message can trigger dozens of subscribers. npayload handles fan-out automatically:

// Each subscriber gets its own delivery with independent retries
// If 1 of 10 subscribers fails, only that subscriber retries
// The other 9 are unaffected

Architecture patterns

Saga orchestration

Coordinate multi-step business transactions with compensating actions:

// Step 1: Reserve inventory
await npayload.messages.publish({
  channel: 'inventory',
  routingKey: 'inventory.reserve',
  groupKey: orderId, // FIFO ordering per order
  payload: { orderId, items, action: 'reserve' },
});

// Step 2: Charge payment (after inventory confirmation)
await npayload.messages.publish({
  channel: 'payments',
  routingKey: 'payment.charge',
  groupKey: orderId,
  payload: { orderId, amount: 59.98, action: 'charge' },
});

// If payment fails, compensate by releasing inventory
await npayload.messages.publish({
  channel: 'inventory',
  routingKey: 'inventory.release',
  groupKey: orderId,
  payload: { orderId, items, action: 'release', reason: 'payment-failed' },
});

CQRS with streams

Use streams for building read-optimized projections:

// Write side: publish commands
await npayload.messages.publish({
  channel: 'user-events',
  payload: { event: 'profile.updated', userId: 'usr_1', changes: { name: 'Alice' } },
});

// Read side: stream consumer builds a projection
const consumer = await npayload.streams.createConsumer({
  channel: 'user-events',
  name: 'user-profile-projection',
  startFrom: 'earliest',
});

const messages = await npayload.streams.pull(consumer.gid, { limit: 100 });
for (const msg of messages) {
  await updateProjection(msg.payload);
}

Priority-based processing

Handle urgent events before routine ones:

// High priority: payment failures
await npayload.messages.publish({
  channel: 'alerts',
  priority: 10,
  payload: { event: 'payment.failed', severity: 'critical' },
});

// Normal priority: daily reports
await npayload.messages.publish({
  channel: 'alerts',
  priority: 1,
  payload: { event: 'report.ready', severity: 'info' },
});

Why npayload for microservices

FeatureBenefit
Fan-outOne event, unlimited subscribers
Guaranteed deliveryRetries + DLQ, messages never lost
Transactional publishAtomic multi-channel events
Message groupsFIFO ordering per entity
Consumer groupsHorizontal scaling of consumers
Circuit breakerProtect failing services automatically
Routing keysFilter events per subscriber
ConnectorsBridge to Kafka, SQS, EventBridge

Next steps

Was this page helpful?

On this page