{"id":26041146,"url":"https://github.com/noahwillson/fastify-messaging","last_synced_at":"2026-05-09T00:02:14.043Z","repository":{"id":281051809,"uuid":"941836256","full_name":"noahwillson/fastify-messaging","owner":"noahwillson","description":"A flexible, extensible messaging framework for Fastify microservices that abstracts away the specific message broker implementation.","archived":false,"fork":false,"pushed_at":"2025-03-06T18:35:53.000Z","size":11,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-03-06T18:37:36.390Z","etag":null,"topics":["fastify","fastify-plugin","message-broker","message-queue","microservices","npm-package","rabbitmq"],"latest_commit_sha":null,"homepage":"https://www.npmjs.com/package/fastify-messaging","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/noahwillson.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2025-03-03T06:09:22.000Z","updated_at":"2025-03-06T18:21:15.000Z","dependencies_parsed_at":"2025-03-06T18:37:55.572Z","dependency_job_id":"cbae0aa3-b457-4564-86a2-2ac62c82733a","html_url":"https://github.com/noahwillson/fastify-messaging","commit_stats":null,"previous_names":["noahwillson/fastify-messaging"],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/noahwillson%2Ffastify-messaging","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/noahwillson%2Ffastify-messaging/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/noahwillson%2Ffastify-messaging/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/noahwillson%2Ffastify-messaging/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/noahwillson","download_url":"https://codeload.github.com/noahwillson/fastify-messaging/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":242394438,"owners_count":20120941,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["fastify","fastify-plugin","message-broker","message-queue","microservices","npm-package","rabbitmq"],"created_at":"2025-03-07T13:19:18.683Z","updated_at":"2026-05-09T00:02:14.036Z","avatar_url":"https://github.com/noahwillson.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# fastify-messaging\n\nA flexible, extensible messaging framework for Fastify microservices that abstracts away the specific message broker implementation.\n\n[![npm version](https://img.shields.io/npm/v/fastify-messaging.svg)](https://www.npmjs.com/package/fastify-messaging)\n[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)\n[![TypeScript](https://img.shields.io/badge/%3C%2F%3E-TypeScript-blue)](https://www.typescriptlang.org/)\n\n## Table of Contents\n\n1. [Features](#features)\n2. [Installation](#installation)\n3. [Quick Start](#quick-start)\n   - [Publisher Service](#publisher-service)\n   - [Consumer Service](#consumer-service)\n4. [API Reference](#api-reference)\n   - [MessagingClient](#messagingclient)\n   - [RabbitMQClient](#rabbitmqclient)\n   - [fastifyMessaging](#fastifymessaging)\n5. [Publishing Messages](#publishing-messages)\n6. [Subscribing to Messages](#subscribing-to-messages)\n7. [Advanced Features](#advanced-features)\n   - [Fanout Exchanges](#fanout-exchanges)\n   - [Custom Exchanges](#custom-exchanges)\n   - [Dead Letter Exchanges](#dead-letter-exchanges-dlx)\n   - [Event Handling](#event-handling)\n   - [Graceful Shutdown](#graceful-shutdown)\n8. [Topic Patterns (RabbitMQ)](#topic-patterns-rabbitmq)\n9. [Error Handling](#error-handling)\n10. [Creating a Custom Provider](#creating-a-custom-provider)\n11. [Best Practices](#best-practices)\n12. [Important Updates and Best Practices](#important-updates-and-best-practices)\n13. [Why Use This Package?](#why-use-this-package)\n14. [Contributing](#contributing)\n15. [License](#license)\n16. [Acknowledgments](#acknowledgments)\n\n## Features\n\n- Abstract messaging interface that can be implemented by different providers\n- Built-in RabbitMQ implementation with advanced features:\n  - Multiple exchange types (topic, fanout)\n  - Dead Letter Exchange (DLX) integration with configurable routing keys\n  - Connection resilience with automatic recovery\n  - Custom exchange support for multi-application scenarios\n  - Configurable logging levels\n  - Event-driven connection lifecycle management\n  - Graceful shutdown handling\n- Fastify plugin for easy integration\n- TypeScript support with generics for message types\n- Automatic reconnection with configurable intervals and exponential backoff\n- Message TTL and priority support\n- Manual/Auto message acknowledgment modes\n- Prefetch configuration for message processing rate\n- Dynamic exchange and queue name configuration\n- Subscription management with queue options\n- **New Features:**\n  - **Reconnect Callback:** Set a callback function to be executed when the messaging client reconnects.\n  - **Logging Levels:** Configurable logging levels for better observability.\n  - **Dynamic Queue Creation:** Create dynamic queues for microservices.\n  - **Subscription with DLX:** Subscribe to messages with Dead Letter Exchange (DLX) support.\n  - **Fanout Exchange Subscription:** Subscribe to events from a fanout exchange using a dynamic queue.\n\n## Installation\n\n```bash\nnpm install fastify-messaging\n```\n\n## Quick Start\n\n### Publisher Service\n\n```typescript\nimport Fastify from \"fastify\";\nimport { RabbitMQClient, fastifyMessaging } from \"fastify-messaging\";\n\nasync function start() {\n  const fastify = Fastify();\n\n  // Create a RabbitMQ client\n  const messagingClient = new RabbitMQClient({\n    url: process.env.RABBITMQ_URL || \"amqp://localhost\",\n    exchange: \"events\",\n    exchangeType: \"topic\",\n    prefetch: 10,\n    heartbeat: 60,\n    deadLetterExchange: \"events.dlx\",\n    deadLetterQueue: \"events.dlq\",\n  });\n\n  // Set up event handlers BEFORE registering the plugin\n  messagingClient.on(\"connected\", () =\u003e {\n    fastify.log.info(\"Connected to RabbitMQ\");\n  });\n\n  messagingClient.on(\"error\", (error) =\u003e {\n    fastify.log.error(\"RabbitMQ error:\", error);\n  });\n\n  // Register the messaging plugin\n  await fastify.register(fastifyMessaging, {\n    client: messagingClient,\n  });\n\n  // Example route that publishes an event\n  fastify.post(\"/orders\", async (request, reply) =\u003e {\n    const order = request.body as any;\n\n    try {\n      // Publish event with proper error handling\n      await fastify.messaging.publish(\"order.created\", {\n        id: order.id,\n        customerId: order.customerId,\n        amount: order.amount,\n        timestamp: new Date().toISOString(),\n      });\n\n      return { success: true, orderId: order.id };\n    } catch (error) {\n      fastify.log.error(\"Failed to publish order event:\", error);\n      reply.code(500);\n      return { success: false, error: \"Failed to process order\" };\n    }\n  });\n\n  await fastify.listen({ port: 3000 });\n}\n\nstart().catch(console.error);\n```\n\n### Consumer Service\n\n```typescript\nimport Fastify from \"fastify\";\nimport { RabbitMQClient, fastifyMessaging } from \"fastify-messaging\";\n\ninterface OrderCreatedEvent {\n  id: string;\n  customerId: string;\n  amount: number;\n  timestamp: string;\n}\n\nasync function start() {\n  const fastify = Fastify();\n\n  // Create a RabbitMQ client\n  const messagingClient = new RabbitMQClient({\n    url: process.env.RABBITMQ_URL || \"amqp://localhost\",\n    exchange: \"events\",\n    exchangeType: \"topic\",\n    prefetch: 10,\n    heartbeat: 60,\n    deadLetterExchange: \"events.dlx\",\n    deadLetterQueue: \"events.dlq\",\n  });\n\n  // Set up event handlers BEFORE registering the plugin\n  messagingClient.on(\"connected\", () =\u003e {\n    fastify.log.info(\"Connected to RabbitMQ\");\n  });\n\n  messagingClient.on(\"error\", (error) =\u003e {\n    fastify.log.error(\"RabbitMQ error:\", error);\n  });\n\n  // Register the messaging plugin\n  await fastify.register(fastifyMessaging, {\n    client: messagingClient,\n  });\n\n  // Subscribe to events\n  const subscriptionId = await fastify.messaging.subscribe\u003cOrderCreatedEvent\u003e(\n    \"order.created\",\n    async (message) =\u003e {\n      try {\n        const order = message.content;\n        fastify.log.info(\n          `Processing order ${order.id} for customer ${order.customerId}`\n        );\n\n        // Process the order...\n\n        // Acknowledge successful processing\n        await message.ack();\n      } catch (error) {\n        fastify.log.error(\"Error processing order:\", error);\n\n        // Choose appropriate action based on error type\n        if (error.name === \"ValidationError\") {\n          // Don't requeue for data validation errors\n          await message.nack(false);\n        } else {\n          // Requeue for possible transient errors\n          await message.nack(true);\n        }\n      }\n    },\n    {\n      queueName: \"order-service-queue\",\n      durable: true,\n      ackMode: \"manual\",\n    }\n  );\n\n  // Handle DLX messages (failed messages)\n  await fastify.messaging.subscribe(\n    \"failed.order.created\",\n    async (message) =\u003e {\n      const headers = message.options?.headers || {};\n      fastify.log.warn(`Processing failed order: ${message.content.id}`);\n      fastify.log.warn(`Error reason: ${headers[\"x-error\"]}`);\n\n      // Process failed message (store in DB, alert, etc.)\n      await message.ack();\n    },\n    {\n      queueName: \"events.dlq\",\n      durable: true,\n      ackMode: \"manual\",\n    }\n  );\n\n  await fastify.listen({ port: 3001 });\n}\n\nstart().catch(console.error);\n```\n\n## API Reference\n\n### MessagingClient\n\nAbstract class that defines the messaging interface.\n\n#### Methods\n\n- `connect()`: Connect to the messaging system\n- `publish\u003cT\u003e(topic: string, message: T, options?: MessageOptions)`: Publish a message\n- `subscribe\u003cT\u003e(topic: string, handler: MessageHandler\u003cT\u003e, options?: SubscriptionOptions)`: Subscribe to messages\n- `unsubscribe(subscriptionId: string)`: Unsubscribe from messages\n- `close()`: Close the connection\n- `on(event: RabbitMQEvents, listener)`: Listen to connection events\n- `onReconnect(callback)`: Set reconnection callback\n- `setLogLevel(level)`: Configure logging verbosity\n- `publishToFanout(eventType, message, options)`: Publish to fanout exchange\n- `subscribeToFanout(eventType, handler, queueName, options)`: Subscribe to fanout exchange\n- `subscribeWithDLX(topic, handler, dlxExchange, dlxQueue, options)`: Subscribe with DLX\n- `gracefulShutdown(timeout)`: Gracefully shutdown connection\n\n### RabbitMQClient\n\nImplementation of `MessagingClient` for RabbitMQ.\n\n#### Constructor Options\n\n```typescript\ninterface RabbitMQConfig {\n  url: string;\n  exchange: string;\n  exchangeType?: \"direct\" | \"topic\" | \"fanout\" | \"headers\";\n  prefetch?: number;\n  vhost?: string;\n  heartbeat?: number;\n  connectionTimeout?: number;\n  maxReconnectAttempts?: number;\n  reconnectBackoffMultiplier?: number;\n  maxReconnectDelay?: number;\n  getExchangeName?: (eventType: string) =\u003e string;\n  getQueueName?: (eventType: string, queueName?: string) =\u003e string;\n  queueOptions?: {\n    durable?: boolean;\n    exclusive?: boolean;\n    autoDelete?: boolean;\n    arguments?: any;\n  };\n  deadLetterExchange?: string;\n  deadLetterQueue?: string;\n  deadLetterRoutingKey?: string; // Routing key for binding DLQ to DLX\n  exchangeOptions?: {\n    alternateExchange?: string;\n    arguments?: any;\n    durable?: boolean;\n    internal?: boolean;\n    autoDelete?: boolean;\n  };\n  initialConnectionRetries?: number; // Number of initial connection attempts\n  initialConnectionDelay?: number; // Delay between initial connection attempts in ms\n}\n```\n\n#### Example\n\n```typescript\nconst client = new RabbitMQClient({\n  url: \"amqp://localhost\",\n  exchange: \"events\",\n  exchangeType: \"topic\",\n  prefetch: 10,\n  reconnectInterval: 5000,\n});\n\nawait client.connect();\n```\n\n### fastifyMessaging\n\nFastify plugin that integrates a messaging client with Fastify.\n\n#### Options\n\n```typescript\ninterface FastifyMessagingOptions {\n  client: MessagingClient;\n}\n```\n\n#### Example\n\n```typescript\nawait fastify.register(fastifyMessaging, {\n  client: messagingClient,\n});\n```\n\n### Message Types\n\n```typescript\n// Message handler function\ninterface MessageHandler\u003cT = any\u003e {\n  (message: Message\u003cT\u003e): Promise\u003cvoid\u003e | void;\n}\n\n// Message object passed to handlers\ninterface Message\u003cT = any\u003e {\n  content: T; // The parsed message content\n  routingKey: string; // The routing key/topic\n  options?: MessageOptions; // Message metadata\n  originalMessage?: any; // Provider-specific message object\n}\n\n// Options for publishing messages\ninterface MessageOptions {\n  headers?: Record\u003cstring, string | number | boolean\u003e;\n  contentType?: string;\n  contentEncoding?: string;\n  persistent?: boolean;\n  expiration?: string | number;\n  priority?: number;\n  correlationId?: string;\n  replyTo?: string;\n  messageId?: string;\n  timestamp?: number;\n  [key: string]: any;\n  expiration?: string | number; // Message TTL\n  priority?: number; // 0-9 priority levels\n}\n\n// Options for subscribing to messages\ninterface SubscriptionOptions {\n  queueName?: string;\n  exclusive?: boolean;\n  durable?: boolean;\n  autoDelete?: boolean;\n  prefetch?: number;\n  ackMode: \"auto\" | \"manual\";\n  exchangeName?: string; // Custom exchange name to override the default\n  exchangeType?: \"direct\" | \"topic\" | \"fanout\" | \"headers\"; // Exchange type for custom exchange\n  dlxRoutingKey?: string; // Routing key for binding DLQ to DLX (for subscribeWithDLX)\n  arguments?: {\n    \"x-message-ttl\"?: number;\n    \"x-expires\"?: number;\n    \"x-max-length\"?: number;\n    \"x-max-length-bytes\"?: number;\n    \"x-overflow\"?: \"drop-head\" | \"reject-publish\";\n    \"x-dead-letter-exchange\"?: string;\n    \"x-dead-letter-routing-key\"?: string;\n    \"x-single-active-consumer\"?: boolean;\n    \"x-max-priority\"?: number;\n    [key: string]: any;\n  };\n}\n```\n\n## Publishing Messages\n\n```typescript\n// Basic publishing\nawait fastify.messaging.publish(\"user.created\", {\n  id: \"123\",\n  name: \"John Doe\",\n  email: \"john@example.com\",\n});\n\n// Publishing with options\nawait fastify.messaging.publish(\n  \"order.shipped\",\n  {\n    orderId: \"456\",\n    trackingNumber: \"TRACK123\",\n  },\n  {\n    persistent: true,\n    priority: 5,\n    headers: {\n      \"x-retry-count\": 0,\n    },\n  }\n);\n```\n\n## Subscribing to Messages\n\n```typescript\n// Basic subscription\nconst subscriptionId = await fastify.messaging.subscribe(\n  \"user.created\",\n  async (message) =\u003e {\n    console.log(`User created: ${message.content.id}`);\n  }\n);\n\n// Subscription with a named queue (for persistent subscriptions)\nconst orderSubscriptionId = await fastify.messaging.subscribe(\n  \"order.*.created\",\n  async (message) =\u003e {\n    console.log(`Order created: ${message.content.id}`);\n  },\n  {\n    queueName: \"order-processing-service\",\n    durable: true,\n  }\n);\n\n// Unsubscribing\nawait fastify.messaging.unsubscribe(subscriptionId);\n```\n\n## Advanced Features\n\n### Fanout Exchanges\n\n```typescript\n// Publish to a fanout exchange\nawait client.publishToFanout(\"broadcast\", { message: \"Hello everyone!\" });\n\n// Subscribe to a fanout exchange\nawait client.subscribeToFanout(\n  \"broadcast\",\n  async (msg) =\u003e {\n    console.log(\"Received broadcast:\", msg.content);\n  },\n  \"broadcast_queue\"\n);\n```\n\n### Custom Exchanges\n\nThe RabbitMQ client supports working with multiple exchanges in a single client instance, which is useful for multi-application scenarios:\n\n```typescript\n// Create a client with a default exchange\nconst client = new RabbitMQClient({\n  url: \"amqp://localhost\",\n  exchange: \"main-exchange\",\n  exchangeType: \"topic\",\n});\n\n// Subscribe to the default exchange\nawait client.subscribe(\"orders.created\", async (msg) =\u003e {\n  console.log(\"Order created:\", msg.content);\n});\n\n// Subscribe to a different exchange\nawait client.subscribe(\n  \"payments.processed\",\n  async (msg) =\u003e {\n    console.log(\"Payment processed:\", msg.content);\n  },\n  {\n    ackMode: \"manual\",\n    exchangeName: \"payments-exchange\", // Custom exchange name\n    exchangeType: \"topic\", // Exchange type (optional)\n  }\n);\n```\n\nKey features:\n\n- **Automatic Exchange Creation**: Custom exchanges are automatically created if they don't exist\n- **Exchange Type Control**: Specify the exchange type for custom exchanges\n- **Multiple Applications**: Use a single client to interact with multiple exchanges\n- **Consistent Configuration**: Exchange options from the main config are applied to custom exchanges\n\n### Dead Letter Exchanges (DLX)\n\nDead Letter Exchanges provide a mechanism to handle messages that cannot be processed successfully. The RabbitMQ client supports comprehensive DLX configuration with the following features:\n\n#### Basic DLX Configuration\n\nConfigure DLX globally when creating the client:\n\n```typescript\nconst client = new RabbitMQClient({\n  url: \"amqp://localhost\",\n  exchange: \"main-exchange\",\n  deadLetterExchange: \"dlx.example\",\n  deadLetterQueue: \"dlq.example\",\n  deadLetterRoutingKey: \"#\", // Optional: Routing key for binding DLQ to DLX\n});\n```\n\n#### Per-Subscription DLX Configuration\n\n```typescript\nawait client.subscribeWithDLX(\n  \"payment.process\",\n  async (msg) =\u003e {\n    // Process payment\n    if (!isValid(msg.content)) {\n      // Message will be sent to DLQ when rejected\n      msg.nack(false); // false = don't requeue\n    }\n  },\n  \"payments_dlx\", // DLX name\n  \"payments_dlq\", // DLQ name\n  {\n    queueName: \"payments\",\n    dlxRoutingKey: \"payments.#\", // Optional: Custom routing key for binding DLQ to DLX\n    ackMode: \"manual\",\n  }\n);\n```\n\n#### Advanced DLX Features\n\n- **Configurable Routing Keys**: Control how messages are routed to and within the DLX\n- **TTL for Dead Letter Queues**: Messages in DLQs expire after 7 days by default\n- **Custom Exchange Types**: DLX uses topic exchange type for flexible routing patterns\n- **Microservice Isolation**: Each service can have its own DLQ with specific routing patterns\n\n#### Processing Failed Messages\n\n```typescript\n// Consume from the DLQ to handle failed messages\nawait client.subscribe(\n  \"#\", // Match all routing keys\n  async (msg) =\u003e {\n    console.log(\"Failed message:\", msg.content);\n    console.log(\"Original routing key:\", msg.fields.routingKey);\n\n    // Process failed message or log it\n    msg.ack();\n  },\n  {\n    ackMode: \"manual\",\n    queueName: \"dlq.example\", // Name of your DLQ\n  }\n);\n```\n\n### Event Handling\n\n```typescript\nclient.on(\"connected\", () =\u003e console.log(\"Connected to RabbitMQ\"));\nclient.on(\"error\", (err) =\u003e console.error(\"RabbitMQ error:\", err));\nclient.on(\"reconnected\", () =\u003e console.log(\"Reconnected successfully\"));\nclient.on(\"connection_permanently_down\", () =\u003e\n  console.log(\"RabbitMQ connection is permanently down\")\n);\n```\n\n### Connection Resilience\n\nThe RabbitMQ client includes robust connection management to ensure your application remains operational even when RabbitMQ is unavailable:\n\n```typescript\n// Configure connection resilience\nconst client = new RabbitMQClient({\n  url: \"amqp://localhost\",\n  exchange: \"main-exchange\",\n  maxReconnectAttempts: 10, // Maximum reconnection attempts before entering permanent down state\n  reconnectBackoffMultiplier: 2, // Multiplier for exponential backoff\n  maxReconnectDelay: 30000, // Maximum delay between reconnection attempts (30 seconds)\n});\n\n// Get connection status\nconst status = client.getConnectionStatus();\nconsole.log(`Connected: ${status.connected}`);\nconsole.log(`Permanent failure: ${status.permanentFailure}`);\nconsole.log(`Retry count: ${status.retryCount}`);\n\n// Listen for connection events\nclient.on(\"connection_permanently_down\", () =\u003e {\n  console.log(\"RabbitMQ connection is permanently down\");\n  // Implement fallback mechanisms or notify monitoring systems\n});\n\n// Manually trigger recovery attempt\nawait client.attemptRecovery();\n```\n\nKey features:\n\n- **Exponential Backoff**: Gradually increases delay between reconnection attempts\n- **Permanent Down State**: After maximum attempts, enters a permanent down state without crashing\n- **Background Recovery**: Periodically attempts recovery in the background\n- **Graceful Degradation**: Publishing attempts return false instead of throwing errors when disconnected\n- **Connection Status**: Provides detailed connection status information\n\n### Graceful Shutdown\n\n```typescript\nprocess.on(\"SIGINT\", async () =\u003e {\n  await client.gracefulShutdown(10000);\n  process.exit(0);\n});\n```\n\n## Topic Patterns (RabbitMQ)\n\nRabbitMQ supports topic patterns with wildcards:\n\n- `*` (star) matches exactly one word\n- `#` (hash) matches zero or more words\n\nExamples:\n\n- `order.created` - Matches only \"order.created\" events\n- `order.*` - Matches \"order.created\", \"order.updated\", etc.\n- `order.#` - Matches \"order.created\", \"order.item.added\", etc.\n- `#` - Matches all messages\n\n## Error Handling\n\nThe framework provides custom error classes for different types of errors:\n\n```typescript\nimport {\n  MessagingError,\n  ConnectionError,\n  PublishError,\n  SubscriptionError,\n} from \"fastify-messaging\";\n\ntry {\n  await fastify.messaging.publish(\"order.created\", { id: \"123\" });\n} catch (error) {\n  if (error instanceof PublishError) {\n    // Handle publishing error\n  } else if (error instanceof ConnectionError) {\n    // Handle connection error\n  } else {\n    // Handle other errors\n  }\n}\n```\n\n## Creating a Custom Provider\n\nYou can create your own messaging provider by implementing the `MessagingClient` abstract class:\n\n```typescript\nimport {\n  MessagingClient,\n  Message,\n  MessageHandler,\n  MessageOptions,\n  SubscriptionOptions,\n} from \"fastify-messaging\";\n\nexport class CustomMessagingClient extends MessagingClient {\n  constructor(config: any) {\n    super(config);\n  }\n\n  async connect(): Promise\u003cvoid\u003e {\n    // Connect to your messaging system\n  }\n\n  async publish\u003cT\u003e(\n    topic: string,\n    message: T,\n    options?: MessageOptions\n  ): Promise\u003cboolean\u003e {\n    // Publish a message\n    return true;\n  }\n\n  async subscribe\u003cT\u003e(\n    topic: string,\n    handler: MessageHandler\u003cT\u003e,\n    options?: SubscriptionOptions\n  ): Promise\u003cstring\u003e {\n    // Subscribe to messages\n    return \"subscription-id\";\n  }\n\n  async unsubscribe(subscriptionId: string): Promise\u003cvoid\u003e {\n    // Unsubscribe from messages\n  }\n\n  async close(): Promise\u003cvoid\u003e {\n    // Close the connection\n  }\n}\n```\n\nThen use it with Fastify:\n\n```typescript\nconst messagingClient = new CustomMessagingClient({\n  // Your custom configuration\n});\n\nawait fastify.register(fastifyMessaging, {\n  client: messagingClient,\n});\n```\n\n## Best Practices\n\n1. **Use TypeScript Interfaces for Events**: Define interfaces for your event types to ensure type safety.\n\n```typescript\ninterface UserCreatedEvent {\n  id: string;\n  name: string;\n  email: string;\n  createdAt: string;\n}\n\nawait fastify.messaging.publish\u003cUserCreatedEvent\u003e(\"user.created\", {\n  id: \"123\",\n  name: \"John Doe\",\n  email: \"john@example.com\",\n  createdAt: new Date().toISOString(),\n});\n```\n\n2. **Use Named Queues for Services**: When you want a service to receive all messages of a certain type, even if it's down temporarily, use a named queue.\n\n```typescript\nawait fastify.messaging.subscribe\u003cOrderCreatedEvent\u003e(\n  \"order.created\",\n  handleOrderCreated,\n  {\n    queueName: \"order-processing-service\",\n    durable: true,\n  }\n);\n```\n\n3. **Use Correlation IDs**: For request-reply patterns or tracking message flows across services.\n\n```typescript\nconst correlationId = uuidv4();\n\nawait fastify.messaging.publish(\n  \"user.get\",\n  { userId: \"123\" },\n  {\n    correlationId,\n    replyTo: \"user-service.replies\",\n  }\n);\n```\n\n4. **Handle Errors Properly**: Always wrap messaging operations in try-catch blocks.\n\n```typescript\ntry {\n  await fastify.messaging.publish(\"order.created\", orderData);\n} catch (error) {\n  fastify.log.error(\"Failed to publish order.created event\", error);\n  // Implement fallback strategy or retry logic\n}\n```\n\n5. **Use DLX for Critical Messages**: Ensure message reliability with dead letter exchanges.\n\n6. **Manual Acknowledgments**: Use ackMode: 'manual' for guaranteed processing.\n\n7. **Priority Queues**: Implement message prioritization for urgent tasks.\n\n```typescript\n// Example priority message\nawait client.publish(\"order.priority\", orderData, {\n  priority: 9,\n  expiration: 3600000, // 1 hour TTL\n});\n\n// Example DLX configuration\nawait client.subscribeWithDLX(\n  \"invoice.generate\",\n  processInvoice,\n  \"invoice_dlx\",\n  \"invoice_dlx_queue\"\n);\n```\n\n8. **Prefetch Tuning**: Adjust prefetch count based on consumer capacity.\n\n9. **Connection Monitoring**: Use event listeners for connection state management.\n\n10. **Structured Logging**: Utilize setLogLevel for production logging.\n\n## Important Updates and Best Practices\n\n1. **Connection Resilience**: The client now supports robust connection management with automatic recovery and permanent down state detection. Use the `connection_permanently_down` event to implement fallback mechanisms.\n\n2. **Custom Exchanges**: You can now subscribe to different exchanges using the same client instance by specifying `exchangeName` and `exchangeType` in subscription options.\n\n3. **Configurable DLX Routing**: Dead Letter Exchange routing is now fully configurable with `deadLetterRoutingKey` and `deadLetterMessageRoutingKey` options.\n\n4. **TTL for Dead Letter Queues**: Messages in Dead Letter Queues now have a default TTL of 7 days to prevent infinite storage.\n\n5. **Exponential Backoff**: Connection retry logic now uses exponential backoff with configurable parameters.\n\n6. **Manual Acknowledgment**: Always use manual acknowledgment mode for critical messages to ensure proper processing.\n\n7. **Graceful Shutdown**: Implement graceful shutdown to ensure in-flight messages are processed before application exit.\n\n8. **Error Handling**: Implement proper error handling in message handlers to prevent unhandled rejections.\n\n9. **Message Serialization**: Be mindful of message serialization limitations; complex objects may lose information.\n\n10. **Topic Naming**: Use consistent topic naming conventions to make routing patterns predictable.\n\n## Why Use This Package?\n\n- **Abstraction**: Decouples your application code from the specific message broker implementation\n- **Future-Proofing**: Switch message brokers without changing your application code\n- **Simplicity**: Clean, intuitive API that's easy to use\n- **Reliability**: Built-in reconnection and error handling\n- **Type Safety**: Full TypeScript support with generics\n- **Enterprise-Ready Features**: DLX, priority queues, TTL, and more\n- **Production Resilience**: Automatic reconnection, graceful shutdown\n- **Enhanced Observability**: Connection events and configurable logging\n- **Flexible Patterns**: Support for both topic and fanout exchanges\n\n## Contributing\n\nContributions are welcome! Please feel free to submit a Pull Request.\n\n1. Fork the repository\n2. Create your feature branch (`git checkout -b feature/amazing-feature`)\n3. Commit your changes (`git commit -m 'Add some amazing feature'`)\n4. Push to the branch (`git push origin feature/amazing-feature`)\n5. Open a Pull Request\n\n## License\n\nThis project is licensed under the MIT License - see the LICENSE file for details.\n\n## Acknowledgments\n\n- [Fastify](https://www.fastify.io/) - The web framework used\n- [amqplib](https://github.com/squaremo/amqp.node) - AMQP 0-9-1 library for Node.js\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnoahwillson%2Ffastify-messaging","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnoahwillson%2Ffastify-messaging","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnoahwillson%2Ffastify-messaging/lists"}