fastify-rabbitmq 0.0.1-alpha.0

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2023 Shane
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,93 @@
1
+ # fastify-rabbitmq
2
+ A Fastify RabbitMQ Plugin Developed in Pure TypeScript. Providing a lot more expandability on the ```amqplib``` package and RabbitMQ functionally.
3
+
4
+ ## Install
5
+ ```
6
+ npm i fastify-rabbitmq
7
+ ```
8
+
9
+ ## Usage
10
+ Register as a plugin. This will decorate your `fastify` instance with the following methods:
11
+
12
+ * fastify.rabbitmq.publishMessage
13
+ * fastify.rabbitmq.directMessage
14
+
15
+ ```js
16
+ import fastifyRabbit from "fastify-rabbitmq"
17
+
18
+ fastify.register(fastifyRabbit, {
19
+ queue: 'rabbitqueue',
20
+ consumeMessageFn: async (message, channel: Channel) => {
21
+ console.log(message.content.toString())
22
+ }
23
+ })
24
+ ```
25
+
26
+ ## Options
27
+
28
+ ```typescript
29
+ export interface FastifyRabbitMQOptions {
30
+ namespace?: string
31
+ queue: string
32
+ host?: string
33
+ port?: string
34
+ username?: string
35
+ password?: string
36
+ preProcessMessageFn?: (message: any) => string;
37
+ consumeMessageFn: (message: ConsumeMessage, channel: Channel) => void;
38
+ confirmChannel?: boolean
39
+ }
40
+ ```
41
+ ### `queue` (required)
42
+
43
+ The RabbitMQ queue to create.
44
+
45
+ ### `consumeMessageFn` (required)
46
+
47
+ How this particular queue will process messages. Rememer that plugins in fastify can not access the request/reply scope. You will need to send your data that you get to a REST API endpoint for it to be processed.
48
+
49
+ ```typescript
50
+ consumeMessageFn: async (message, channel: Channel) => {
51
+ const data = message.content.toString()
52
+ const options: any = {
53
+ json: {
54
+ ...data
55
+ },
56
+ };
57
+ const {body} = await got.post(`http://localhost:3000/foo`, options)
58
+ }
59
+ ```
60
+ One example using the ```got``` NPM package to then pass the results from theis RabbitMQ data.
61
+
62
+ ### `name`
63
+
64
+ If you register more than one RabbitMQ queue within the same application, you must give it a namespace.
65
+ The name can be the same as the queue.
66
+
67
+ ### `host`
68
+
69
+ By default, it's ```localhost``` but pass your RabbitMQ server hostname or IP address.
70
+
71
+ ### `port`
72
+
73
+ By default it's ```5672``` but pass your RabbitMQ port.
74
+
75
+ ### `username`
76
+
77
+ By default, it's ```guest``` but pass your RabbitMQ username.
78
+
79
+ ### `password`
80
+
81
+ By default, it's ```guest``` but pass your RabbitMQ password.
82
+
83
+ ### `preProcessMessageFn`
84
+
85
+ Before your message goes out, do something to your message.
86
+ This is good so all your message go out correctly.
87
+ If not, just make sure you are only sending strings out.
88
+
89
+ ### `confirmChannel`
90
+
91
+ By default,
92
+ it's ```false``` but mark this ture in order for you to mark your queue
93
+ always confirm that it received data from a remote RabbitMQ.
@@ -0,0 +1,33 @@
1
+ import { FastifyInstance, FastifyPluginCallback } from 'fastify';
2
+ import { Channel, ConsumeMessage } from 'amqplib';
3
+ import FastifyRabbitMQOptions = fastifyRabbitMQ.FastifyRabbitMQOptions;
4
+ declare module 'fastify' {
5
+ interface FastifyInstance {
6
+ rabbitmq: fastifyRabbitMQ.FastifyRabbitMQObject & fastifyRabbitMQ.FastifyRabbitMQNestedObject;
7
+ }
8
+ }
9
+ type fastifyRabbitMQPlugin = FastifyPluginCallback<NonNullable<fastifyRabbitMQ.FastifyRabbitMQOptions>>;
10
+ declare namespace fastifyRabbitMQ {
11
+ export interface FastifyRabbitMQObject {
12
+ publishMessage(sendQueue: string, message: any): void;
13
+ directMessage(sendQueue: string, message: any): void;
14
+ }
15
+ export interface FastifyRabbitMQNestedObject {
16
+ [namespace: string]: FastifyRabbitMQObject;
17
+ }
18
+ export interface FastifyRabbitMQOptions {
19
+ namespace?: string;
20
+ queue: string;
21
+ host?: string;
22
+ port?: string;
23
+ username?: string;
24
+ password?: string;
25
+ preProcessMessageFn?: (message: any) => string;
26
+ consumeMessageFn: (message: ConsumeMessage, channel: Channel) => void;
27
+ confirmChannel?: boolean;
28
+ }
29
+ export const fastifyRabbitMQPlugin: fastifyRabbitMQPlugin;
30
+ export { fastifyRabbitMQPlugin as default };
31
+ }
32
+ declare const fastifyRabbit: (fastify: FastifyInstance, options: FastifyRabbitMQOptions) => Promise<void>;
33
+ export default fastifyRabbit;
package/dist/index.js ADDED
@@ -0,0 +1,113 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ import fp from 'fastify-plugin';
11
+ import amqp from 'amqplib';
12
+ /**
13
+ * decorateFastifyInstance
14
+ * @since 0.0.1
15
+ * @param fastify
16
+ * @param options
17
+ * @param functions
18
+ */
19
+ const decorateFastifyInstance = (fastify, options, functions) => {
20
+ const { namespace } = options;
21
+ if (namespace) {
22
+ if (typeof fastify.rabbitmq === 'undefined') {
23
+ fastify.decorate('rabbitmq', Object.assign({}, functions));
24
+ }
25
+ if (typeof fastify.rabbitmq[namespace] !== 'undefined') {
26
+ throw Error('Connection name already registered: ' + namespace);
27
+ }
28
+ }
29
+ else {
30
+ if (typeof fastify.rabbitmq !== 'undefined') {
31
+ throw Error('fastify-rabbitmq has already registered');
32
+ }
33
+ }
34
+ if (typeof fastify.rabbitmq === 'undefined') {
35
+ fastify.decorate('rabbitmq', Object.assign({}, functions));
36
+ }
37
+ };
38
+ const fastifyRabbit = fp((fastify, options) => __awaiter(void 0, void 0, void 0, function* () {
39
+ const { queue, host = 'localhost', port = '5672', username = 'guest', password = 'guest', confirmChannel = false, consumeMessageFn, preProcessMessageFn } = options;
40
+ const RABBITMQ_FULL_URL = `amqp://${username}:${password}@${host}:${port}`;
41
+ /**
42
+ * getRabbitChannel
43
+ * @since 0.0.1
44
+ * @param confirmChannel {boolean} Set this to true if you want the channel to always ask for a confirmation that it was sent and received by the other side.
45
+ */
46
+ function getRabbitChannel(confirmChannel) {
47
+ return __awaiter(this, void 0, void 0, function* () {
48
+ const conn = yield amqp.connect(RABBITMQ_FULL_URL);
49
+ return confirmChannel ? yield conn.createConfirmChannel() : yield conn.createChannel();
50
+ });
51
+ }
52
+ /**
53
+ * consumeRabbitMessage
54
+ * Process the incoming message from another queue. This is required.
55
+ * @since 0.0.1
56
+ * @param msg {ConsumeMessage} Data from RabbitMQ. It's in a string format; however, you need to interpret the data.
57
+ * @param channel {Channel} What channel did it comes from and the properties of that channel.
58
+ */
59
+ function consumeRabbitMessage(msg, channel) {
60
+ return __awaiter(this, void 0, void 0, function* () {
61
+ if (typeof consumeMessageFn === 'function') {
62
+ consumeMessageFn(msg, channel);
63
+ }
64
+ });
65
+ }
66
+ /**
67
+ * preProcessMessage
68
+ * If the parameter preProcessMessageFn is passed, it will use that function to process the data.
69
+ * @since 0.0.1
70
+ * @param message {any} The data you want to send, however it must be a string.
71
+ */
72
+ function preProcessMessage(message) {
73
+ if (typeof preProcessMessageFn === 'function') {
74
+ return preProcessMessageFn(message);
75
+ }
76
+ else {
77
+ return message;
78
+ }
79
+ }
80
+ const channel = yield getRabbitChannel(confirmChannel);
81
+ yield (channel === null || channel === void 0 ? void 0 : channel.assertQueue(queue, { durable: false }).then(() => __awaiter(void 0, void 0, void 0, function* () {
82
+ return yield channel.consume(queue, (msg) => __awaiter(void 0, void 0, void 0, function* () {
83
+ yield consumeRabbitMessage(msg, channel);
84
+ }), {
85
+ noAck: true
86
+ });
87
+ })));
88
+ /**
89
+ * publishMessage
90
+ * @since 0.0.1
91
+ * @param sendQueue {string} The target queue.
92
+ * @param message {any} You can publish anything as long as it goes out over as a string.
93
+ */
94
+ function publishMessage(sendQueue, message) {
95
+ channel === null || channel === void 0 ? void 0 : channel.publish('', sendQueue, Buffer.from(preProcessMessage(message)));
96
+ }
97
+ /**
98
+ * directMessage
99
+ * @since 0.0.1
100
+ * @param sendQueue {string} The target queue and directed to that queue. No one else can take it.
101
+ * @param message {any} You can publish anything as long as it goes out over as a string.
102
+ */
103
+ function directMessage(sendQueue, message) {
104
+ channel === null || channel === void 0 ? void 0 : channel.sendToQueue(sendQueue, Buffer.from(preProcessMessage(message)), {
105
+ replyTo: queue
106
+ });
107
+ }
108
+ /**
109
+ * Decorate Fastify
110
+ */
111
+ decorateFastifyInstance(fastify, options, { publishMessage, directMessage });
112
+ }));
113
+ export default fastifyRabbit;
package/index.ts ADDED
@@ -0,0 +1,165 @@
1
+ import {FastifyInstance, FastifyPluginCallback} from 'fastify'
2
+ import fp from 'fastify-plugin'
3
+ import amqp, {Channel, ConsumeMessage} from 'amqplib'
4
+
5
+ import FastifyRabbitMQOptions = fastifyRabbitMQ.FastifyRabbitMQOptions;
6
+
7
+ declare module 'fastify' {
8
+ interface FastifyInstance {
9
+ rabbitmq: fastifyRabbitMQ.FastifyRabbitMQObject & fastifyRabbitMQ.FastifyRabbitMQNestedObject;
10
+ }
11
+
12
+ }
13
+
14
+ type fastifyRabbitMQPlugin = FastifyPluginCallback<
15
+ NonNullable<fastifyRabbitMQ.FastifyRabbitMQOptions>
16
+ >;
17
+
18
+ declare namespace fastifyRabbitMQ {
19
+
20
+ export interface FastifyRabbitMQObject {
21
+ publishMessage (sendQueue: string, message: any): void
22
+ directMessage (sendQueue: string, message: any): void
23
+ }
24
+
25
+ export interface FastifyRabbitMQNestedObject {
26
+ [namespace: string]: FastifyRabbitMQObject;
27
+ }
28
+
29
+ export interface FastifyRabbitMQOptions {
30
+ namespace?: string
31
+ queue: string
32
+ host?: string
33
+ port?: string
34
+ username?: string
35
+ password?: string
36
+ preProcessMessageFn?: (message: any) => string;
37
+ consumeMessageFn: (message: ConsumeMessage, channel: Channel) => void;
38
+ confirmChannel?: boolean
39
+ }
40
+
41
+ export const fastifyRabbitMQPlugin: fastifyRabbitMQPlugin
42
+ export { fastifyRabbitMQPlugin as default }
43
+
44
+ }
45
+
46
+ /**
47
+ * decorateFastifyInstance
48
+ * @since 0.0.1
49
+ * @param fastify
50
+ * @param options
51
+ * @param functions
52
+ */
53
+ const decorateFastifyInstance = (fastify: FastifyInstance, options: FastifyRabbitMQOptions, functions: any): void => {
54
+ const { namespace } = options
55
+
56
+ if (namespace) {
57
+ if (typeof fastify.rabbitmq === 'undefined') {
58
+ fastify.decorate('rabbitmq', {...functions})
59
+ }
60
+ if (typeof fastify.rabbitmq[namespace] !== 'undefined') {
61
+ throw Error('Connection name already registered: ' + namespace)
62
+ }
63
+
64
+ } else {
65
+ if (typeof fastify.rabbitmq !== 'undefined') {
66
+ throw Error('fastify-rabbitmq has already registered')
67
+ }
68
+ }
69
+
70
+ if (typeof fastify.rabbitmq === 'undefined') {
71
+ fastify.decorate('rabbitmq', {...functions})
72
+ }
73
+
74
+ }
75
+
76
+ const fastifyRabbit = fp(async (fastify: FastifyInstance, options: FastifyRabbitMQOptions): Promise<void> => {
77
+ const {
78
+ queue,
79
+ host = 'localhost',
80
+ port = '5672',
81
+ username = 'guest',
82
+ password = 'guest',
83
+ confirmChannel = false,
84
+ consumeMessageFn,
85
+ preProcessMessageFn
86
+ } = options
87
+
88
+ const RABBITMQ_FULL_URL = `amqp://${username}:${password}@${host}:${port}`
89
+
90
+ /**
91
+ * getRabbitChannel
92
+ * @since 0.0.1
93
+ * @param confirmChannel {boolean} Set this to true if you want the channel to always ask for a confirmation that it was sent and received by the other side.
94
+ */
95
+ async function getRabbitChannel (confirmChannel: boolean): Promise<Channel> {
96
+ const conn = await amqp.connect(RABBITMQ_FULL_URL)
97
+ return confirmChannel ? await conn.createConfirmChannel() : await conn.createChannel()
98
+ }
99
+
100
+ /**
101
+ * consumeRabbitMessage
102
+ * Process the incoming message from another queue. This is required.
103
+ * @since 0.0.1
104
+ * @param msg {ConsumeMessage} Data from RabbitMQ. It's in a string format; however, you need to interpret the data.
105
+ * @param channel {Channel} What channel did it comes from and the properties of that channel.
106
+ */
107
+ async function consumeRabbitMessage (msg: ConsumeMessage, channel: Channel): Promise<void> {
108
+ if (typeof consumeMessageFn === 'function') {
109
+ consumeMessageFn(msg, channel)
110
+ }
111
+ }
112
+
113
+ /**
114
+ * preProcessMessage
115
+ * If the parameter preProcessMessageFn is passed, it will use that function to process the data.
116
+ * @since 0.0.1
117
+ * @param message {any} The data you want to send, however it must be a string.
118
+ */
119
+ function preProcessMessage(message: any): string {
120
+ if (typeof preProcessMessageFn === 'function') {
121
+ return preProcessMessageFn(message)
122
+ } else {
123
+ return message
124
+ }
125
+ }
126
+
127
+ const channel = await getRabbitChannel(confirmChannel)
128
+
129
+ await channel?.assertQueue(queue, { durable: false }).then(async () => {
130
+ return await channel.consume(queue, async (msg:amqp.ConsumeMessage | null): Promise<void> => {
131
+ await consumeRabbitMessage(<ConsumeMessage>msg, channel)
132
+ }, {
133
+ noAck: true
134
+ })
135
+ })
136
+
137
+ /**
138
+ * publishMessage
139
+ * @since 0.0.1
140
+ * @param sendQueue {string} The target queue.
141
+ * @param message {any} You can publish anything as long as it goes out over as a string.
142
+ */
143
+ function publishMessage (sendQueue: string, message: any): void {
144
+ channel?.publish('', sendQueue, Buffer.from(preProcessMessage(message)))
145
+ }
146
+
147
+ /**
148
+ * directMessage
149
+ * @since 0.0.1
150
+ * @param sendQueue {string} The target queue and directed to that queue. No one else can take it.
151
+ * @param message {any} You can publish anything as long as it goes out over as a string.
152
+ */
153
+ function directMessage (sendQueue: string, message: any): void {
154
+ channel?.sendToQueue(sendQueue, Buffer.from(preProcessMessage(message)), {
155
+ replyTo: queue
156
+ })
157
+ }
158
+
159
+ /**
160
+ * Decorate Fastify
161
+ */
162
+ decorateFastifyInstance(fastify, options, {publishMessage, directMessage})
163
+ })
164
+
165
+ export default fastifyRabbit
package/package.json ADDED
@@ -0,0 +1,47 @@
1
+ {
2
+ "name": "fastify-rabbitmq",
3
+ "version": "0.0.1-alpha.0",
4
+ "description": "A Fastify RabbitMQ Plugin Developed in Pure TypeScript using the AMQPLIB",
5
+ "type": "module",
6
+ "types": "dist/index.d.ts",
7
+ "main": "dist/index.js",
8
+ "scripts": {
9
+ "build": "tsc",
10
+ "build:watch": "tsc -w",
11
+ "lint": "ts-standard",
12
+ "lint:fix": "ts-standard --fix",
13
+ "test": "npm run unit && npm run typescript",
14
+ "update": "npx npm-check-updates -u && npm install"
15
+ },
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "git+https://github.com/Bugs5382/fastify-rabbitmq.git"
19
+ },
20
+ "keywords": [
21
+ "typescript",
22
+ "rabbitmq",
23
+ "fastify",
24
+ "fastify-plugin"
25
+ ],
26
+ "author": "Shane Froebel",
27
+ "license": "MIT",
28
+ "bugs": {
29
+ "url": "https://github.com/Bugs5382/fastify-rabbitmq/issues"
30
+ },
31
+ "homepage": "https://github.com/Bugs5382/fastify-rabbitmq#readme",
32
+ "dependencies": {
33
+ "amqplib": "^0.10.3",
34
+ "fastify-plugin": "^4.5.1"
35
+ },
36
+ "devDependencies": {
37
+ "@types/amqplib": "^0.10.1",
38
+ "@types/node": "^20.4.6",
39
+ "fastify": "^4.21.0",
40
+ "npm-check-updates": "^16.10.17",
41
+ "pre-commit": "^1.2.2",
42
+ "tap": "^16.3.8",
43
+ "ts-standard": "^12.0.2",
44
+ "typescript": "^5.1.6"
45
+ },
46
+ "pre-commit": []
47
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,29 @@
1
+ {
2
+ "files": ["index.ts"],
3
+ "compilerOptions": {
4
+ "target": "es2015",
5
+ "module": "es2015",
6
+ "moduleResolution": "node",
7
+ "declaration": true,
8
+ "outDir": "./dist",
9
+ "noEmit": false,
10
+ "strict": true,
11
+ "noImplicitAny": true,
12
+ "strictNullChecks": true,
13
+ "strictFunctionTypes": true,
14
+ "strictBindCallApply": true,
15
+ "strictPropertyInitialization": true,
16
+ "noImplicitThis": true,
17
+ "alwaysStrict": true,
18
+ "noUnusedLocals": true,
19
+ "noUnusedParameters": true,
20
+ "noImplicitReturns": true,
21
+ "noFallthroughCasesInSwitch": true,
22
+ "noUncheckedIndexedAccess": true,
23
+ "noImplicitOverride": true,
24
+ "noPropertyAccessFromIndexSignature": true,
25
+ "esModuleInterop": true,
26
+ "forceConsistentCasingInFileNames": true,
27
+ "skipLibCheck": true
28
+ }
29
+ }