@kaushverse/rabbitmq-core 1.0.5 → 1.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +98 -0
- package/dist/index.js +27 -17
- package/package.json +3 -2
package/README.md
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# @kaushverse/rabbitmq-core
|
|
2
|
+
|
|
3
|
+
Production-ready RabbitMQ SDK for Node.js microservices.
|
|
4
|
+
|
|
5
|
+
This package provides a clean and opinionated way to **connect**, **publish**, **consume**, and **monitor RabbitMQ**, while handling **TLS**, **logging**, **health checks**, and **graceful shutdown** automatically.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Features
|
|
10
|
+
|
|
11
|
+
- Single RabbitMQ connection per service
|
|
12
|
+
- Supports `amqp://` and `amqps://`
|
|
13
|
+
- Message publishing and consuming helpers
|
|
14
|
+
- Built-in bootstrap for service lifecycle
|
|
15
|
+
- Structured logger (pretty logs in dev, JSON in prod)
|
|
16
|
+
- Health check helper for monitoring
|
|
17
|
+
- Graceful shutdown (SIGINT / SIGTERM)
|
|
18
|
+
- Framework agnostic (Express, Fastify, NestJS)
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## Installation
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
npm install @kaushverse/rabbitmq-core
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Full Express Service Example
|
|
29
|
+
|
|
30
|
+
Below is a complete example showing how to use `@kaushverse/rabbitmq-core`
|
|
31
|
+
inside an Express-based microservice with health checks and publishing routes.
|
|
32
|
+
|
|
33
|
+
### Example: `order-service`
|
|
34
|
+
|
|
35
|
+
```ts
|
|
36
|
+
import "dotenv/config";
|
|
37
|
+
import express from "express";
|
|
38
|
+
import { bootstrap, rabbitHealth } from "@kaushverse/rabbitmq-core";
|
|
39
|
+
|
|
40
|
+
const app = express();
|
|
41
|
+
app.use(express.json());
|
|
42
|
+
|
|
43
|
+
// Health check endpoint
|
|
44
|
+
app.get("/health", (_, res) => {
|
|
45
|
+
res.json({
|
|
46
|
+
status: "ok",
|
|
47
|
+
...rabbitHealth(),
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
// Bootstrap the service
|
|
52
|
+
bootstrap({
|
|
53
|
+
serviceName: "order-service",
|
|
54
|
+
rabbit: {
|
|
55
|
+
url: process.env.RABBITMQ_URL!,
|
|
56
|
+
tls: {
|
|
57
|
+
caPath: process.env.RABBITMQ_CA_PATH,
|
|
58
|
+
servername: process.env.RABBITMQ_SERVERNAME,
|
|
59
|
+
rejectUnauthorized: false, // set true in production
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
start: () => {
|
|
63
|
+
app.listen(3000, () => {
|
|
64
|
+
console.log("🚀 API running on port 3000");
|
|
65
|
+
});
|
|
66
|
+
},
|
|
67
|
+
});
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
```js
|
|
71
|
+
RABBITMQ_URL=amqps://user:password@mq.example.com:5671/vhost
|
|
72
|
+
RABBITMQ_CA_PATH=./certs/ca.pem
|
|
73
|
+
RABBITMQ_SERVERNAME=mq.example.com
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Publishing Messages
|
|
77
|
+
|
|
78
|
+
Use `publishMessage` to publish events to RabbitMQ.
|
|
79
|
+
The SDK automatically handles channels, serialization, and persistence.
|
|
80
|
+
|
|
81
|
+
### Import
|
|
82
|
+
|
|
83
|
+
```js
|
|
84
|
+
import { publishMessage } from "@kaushverse/rabbitmq-core";
|
|
85
|
+
|
|
86
|
+
await publishMessage({
|
|
87
|
+
exchange: "order.events",
|
|
88
|
+
type: "topic",
|
|
89
|
+
routingKey: "order.created",
|
|
90
|
+
message: {
|
|
91
|
+
orderId: "ORD-123",
|
|
92
|
+
amount: 499,
|
|
93
|
+
},
|
|
94
|
+
headers: {
|
|
95
|
+
source: "order-service",
|
|
96
|
+
},
|
|
97
|
+
});
|
|
98
|
+
```
|
package/dist/index.js
CHANGED
|
@@ -61,39 +61,50 @@ function loadCA(caPath) {
|
|
|
61
61
|
// src/connection.ts
|
|
62
62
|
var connection = null;
|
|
63
63
|
var channel = null;
|
|
64
|
-
var connected = false;
|
|
65
64
|
var rabbitEvents = new import_events.EventEmitter();
|
|
65
|
+
rabbitEvents.setMaxListeners(20);
|
|
66
66
|
async function connectRabbitMQ(options) {
|
|
67
|
-
if (
|
|
67
|
+
if (connection && channel) return channel;
|
|
68
68
|
try {
|
|
69
|
-
const
|
|
70
|
-
|
|
71
|
-
|
|
69
|
+
const isSecure = options.url.startsWith("amqps://");
|
|
70
|
+
if (!isSecure && options.tls) {
|
|
71
|
+
console.warn("\u26A0\uFE0F TLS options ignored for amqp:// connection");
|
|
72
|
+
}
|
|
73
|
+
const socketOptions = isSecure ? {
|
|
74
|
+
ca: loadCA(options.tls?.caPath),
|
|
72
75
|
servername: options.tls?.servername,
|
|
73
76
|
rejectUnauthorized: options.tls?.rejectUnauthorized ?? true
|
|
74
|
-
}
|
|
77
|
+
} : void 0;
|
|
78
|
+
connection = await import_amqplib.default.connect(options.url, socketOptions);
|
|
75
79
|
channel = await connection.createChannel();
|
|
76
|
-
connected = true;
|
|
77
80
|
rabbitEvents.emit("connected");
|
|
78
81
|
connection.on("close", () => {
|
|
79
|
-
|
|
82
|
+
connection = null;
|
|
80
83
|
channel = null;
|
|
81
84
|
rabbitEvents.emit("disconnected");
|
|
82
85
|
});
|
|
83
86
|
connection.on("error", (err) => {
|
|
84
|
-
|
|
87
|
+
rabbitEvents.emit("error", err);
|
|
88
|
+
});
|
|
89
|
+
channel.on("close", () => {
|
|
90
|
+
channel = null;
|
|
91
|
+
});
|
|
92
|
+
channel.on("error", (err) => {
|
|
85
93
|
rabbitEvents.emit("error", err);
|
|
86
94
|
});
|
|
87
95
|
return channel;
|
|
88
96
|
} catch (err) {
|
|
89
|
-
|
|
97
|
+
connection = null;
|
|
98
|
+
channel = null;
|
|
90
99
|
rabbitEvents.emit("error", err);
|
|
91
100
|
throw err;
|
|
92
101
|
}
|
|
93
102
|
}
|
|
94
103
|
function getChannel() {
|
|
95
104
|
if (!channel) {
|
|
96
|
-
throw new Error(
|
|
105
|
+
throw new Error(
|
|
106
|
+
"\u274C RabbitMQ channel not available. Call connectRabbitMQ()"
|
|
107
|
+
);
|
|
97
108
|
}
|
|
98
109
|
return channel;
|
|
99
110
|
}
|
|
@@ -104,12 +115,11 @@ async function closeRabbitMQ() {
|
|
|
104
115
|
} finally {
|
|
105
116
|
channel = null;
|
|
106
117
|
connection = null;
|
|
107
|
-
connected = false;
|
|
108
118
|
rabbitEvents.emit("disconnected");
|
|
109
119
|
}
|
|
110
120
|
}
|
|
111
121
|
function isRabbitConnected() {
|
|
112
|
-
return
|
|
122
|
+
return Boolean(connection && channel);
|
|
113
123
|
}
|
|
114
124
|
|
|
115
125
|
// src/consumer.ts
|
|
@@ -219,7 +229,7 @@ async function bootstrap({
|
|
|
219
229
|
await connectRabbitMQ(rabbit);
|
|
220
230
|
logger.info("\u{1F407} RabbitMQ connected");
|
|
221
231
|
await start();
|
|
222
|
-
logger.info(
|
|
232
|
+
logger.info(`\u{1F680} ${serviceName2} started successfully`);
|
|
223
233
|
const shutdown = async (signal) => {
|
|
224
234
|
logger.warn("\u{1F6D1} Shutdown signal received", { signal });
|
|
225
235
|
await closeRabbitMQ();
|
|
@@ -239,12 +249,12 @@ async function bootstrap({
|
|
|
239
249
|
|
|
240
250
|
// src/health/index.ts
|
|
241
251
|
function rabbitHealth() {
|
|
242
|
-
const
|
|
252
|
+
const connected = isRabbitConnected();
|
|
243
253
|
return {
|
|
244
254
|
"\u{1F407}": {
|
|
245
255
|
service: "rabbitmq",
|
|
246
|
-
status:
|
|
247
|
-
healthy:
|
|
256
|
+
status: connected ? "\u{1F7E2} up" : "\u{1F534} down",
|
|
257
|
+
healthy: connected
|
|
248
258
|
}
|
|
249
259
|
};
|
|
250
260
|
}
|
package/package.json
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kaushverse/rabbitmq-core",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.7",
|
|
4
4
|
"description": "Reusable RabbitMQ (AMQP / AMQPS) core for Node.js & microservices",
|
|
5
5
|
"author": "Kaushik (KaushVerse)",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"main": "dist/index.js",
|
|
8
8
|
"types": "dist/index.d.ts",
|
|
9
9
|
"files": [
|
|
10
|
-
"dist"
|
|
10
|
+
"dist",
|
|
11
|
+
"README.md"
|
|
11
12
|
],
|
|
12
13
|
"scripts": {
|
|
13
14
|
"build": "tsup src/index.ts --dts",
|