@nestjs-redis/socket.io-adapter 0.0.21

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) 2025 Saba Pochkhua
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,174 @@
1
+ <div align="center">
2
+
3
+ <img src="https://raw.githubusercontent.com/CSenshi/nestjs-redis/main/docs/images/logo.png" alt="NestJS Redis Toolkit Logo" width="200" height="200">
4
+
5
+ # @nestjs-redis/socket.io-adapter
6
+
7
+ Redis-powered Socket.IO adapter for NestJS enabling scalable WebSocket connections across multiple instances.
8
+
9
+ [![npm version](https://badge.fury.io/js/%40nestjs-redis%2Fsocket.io-adapter.svg)](https://www.npmjs.com/package/@nestjs-redis/socket.io-adapter)
10
+ [![npm downloads](https://img.shields.io/npm/dm/@nestjs-redis/socket.io-adapter.svg)](https://www.npmjs.com/package/@nestjs-redis/socket.io-adapter)
11
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
12
+ [![TypeScript](https://img.shields.io/badge/TypeScript-Ready-blue.svg)](https://www.typescriptlang.org/)
13
+ [![NestJS](https://img.shields.io/badge/NestJS-9%2B-red.svg)](https://nestjs.com/) [![Redis](https://img.shields.io/badge/Redis-5+-red.svg)](https://redis.io/)
14
+
15
+ </div>
16
+
17
+ ---
18
+
19
+ ## Features
20
+
21
+ - **Horizontal scaling**: Connect clients to any server instance
22
+ - **Redis pub/sub**: Automatic event distribution across instances
23
+ - **Lifecycle management**: Redis connections are managed automatically
24
+ - **Works with existing connections**: Integrates seamlessly with `@nestjs-redis/client`
25
+ - **Type-safe**: Full TypeScript support
26
+ - **Production-ready**: Built on the official Socket.IO Redis adapter
27
+
28
+ ## Installation
29
+
30
+ ```bash
31
+ npm install @nestjs-redis/socket.io-adapter
32
+ # or
33
+ yarn add @nestjs-redis/socket.io-adapter
34
+ # or
35
+ pnpm add @nestjs-redis/socket.io-adapter
36
+ ```
37
+
38
+ ## Quick Start
39
+
40
+ ### Setup with existing Redis connection (Recommended)
41
+
42
+ ```typescript
43
+ // app.module.ts
44
+ import { Module } from '@nestjs/common';
45
+ import { RedisModule } from '@nestjs-redis/client';
46
+
47
+ @Module({
48
+ imports: [
49
+ RedisModule.forRoot({
50
+ options: { url: 'redis://localhost:6379' },
51
+ }),
52
+ ],
53
+ })
54
+ export class AppModule {}
55
+ ```
56
+
57
+ ```typescript
58
+ // main.ts
59
+ import { NestFactory } from '@nestjs/core';
60
+ import { setupRedisAdapter } from '@nestjs-redis/socket.io-adapter';
61
+ import { AppModule } from './app.module';
62
+
63
+ async function bootstrap() {
64
+ const app = await NestFactory.create(AppModule);
65
+
66
+ // Setup Redis adapter for Socket.IO
67
+ await setupRedisAdapter(app);
68
+
69
+ await app.listen(3000);
70
+ }
71
+ bootstrap();
72
+ ```
73
+
74
+ ### Multiple Redis connections
75
+
76
+ If you have multiple Redis connections, specify which one to use:
77
+
78
+ ```typescript
79
+ // app.module.ts
80
+ @Module({
81
+ imports: [
82
+ RedisModule.forRoot({
83
+ options: { url: 'redis://localhost:6379' },
84
+ }),
85
+ RedisModule.forRoot({
86
+ connectionName: 'websockets',
87
+ options: { url: 'redis://websockets:6379' },
88
+ }),
89
+ ],
90
+ })
91
+ export class AppModule {}
92
+ ```
93
+
94
+ ```typescript
95
+ // main.ts
96
+ async function bootstrap() {
97
+ const app = await NestFactory.create(AppModule);
98
+
99
+ // Use the 'websockets' Redis connection
100
+ await setupRedisAdapter(app, 'websockets');
101
+
102
+ await app.listen(3000);
103
+ }
104
+ ```
105
+
106
+ ## The Problem
107
+
108
+ When scaling your NestJS application horizontally with multiple instances, WebSocket connections become a challenge. By default, Socket.IO connections are tied to a single server instance, which means:
109
+
110
+ - Events sent from one server instance won't reach clients connected to other instances
111
+ - Real-time features break when users connect to different servers
112
+ - Load balancing becomes complex as you need sticky sessions
113
+
114
+ ## The Solution
115
+
116
+ This package provides a Redis-backed Socket.IO adapter that uses Redis pub/sub to synchronize events across all server instances. When a server emits an event, it's published to Redis and distributed to all other server instances, ensuring all clients receive the event regardless of which server they're connected to.
117
+
118
+ ## How It Works
119
+
120
+ 1. **Redis Pub/Sub**: The adapter creates two Redis connections - one for publishing and one for subscribing
121
+ 2. **Event Distribution**: When a server emits an event, it's published to a Redis channel
122
+ 3. **Cross-Instance Delivery**: All server instances subscribe to the same channels and forward events to their connected clients
123
+ 4. **Automatic Management**: Connection lifecycle is handled automatically by the adapter
124
+
125
+ ## API
126
+
127
+ ### `setupRedisAdapter(app, redisToken?)`
128
+
129
+ Sets up the Redis adapter for the NestJS application.
130
+
131
+ - `app`: NestJS application instance
132
+ - `redisToken` (optional): Redis connection name (defaults to the default connection)
133
+
134
+ ### `RedisIoAdapter`
135
+
136
+ The underlying Socket.IO adapter class that handles Redis connections.
137
+
138
+ ## Architecture
139
+
140
+ ```
141
+ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐
142
+ │ Server 1 │ │ Server 2 │ │ Server 3 │
143
+ │ ┌───────┐ │ │ ┌───────┐ │ │ ┌───────┐ │
144
+ │ │Client │ │ │ │Client │ │ │ │Client │ │
145
+ │ └───────┘ │ │ └───────┘ │ │ └───────┘ │
146
+ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘
147
+ │ │ │
148
+ └──────────────────┼──────────────────┘
149
+
150
+ ┌─────▼─────┐
151
+ │ Redis │
152
+ │ Pub/Sub │
153
+ └───────────┘
154
+ ```
155
+
156
+ ## Learn More
157
+
158
+ - [NestJS WebSocket Adapter Documentation](https://docs.nestjs.com/websockets/adapter)
159
+ - [Socket.IO Redis Adapter](https://socket.io/docs/v4/redis-adapter/)
160
+ - [Redis Pub/Sub](https://redis.io/docs/manual/pubsub/)
161
+
162
+ ## Links
163
+
164
+ - Root repo: [CSenshi/nestjs-redis](https://github.com/CSenshi/nestjs-redis)
165
+ - Issues: [GitHub Issues](https://github.com/CSenshi/nestjs-redis/issues)
166
+ - Discussions: [GitHub Discussions](https://github.com/CSenshi/nestjs-redis/discussions)
167
+
168
+ ## Contributing
169
+
170
+ Please see the [root contributing guidelines](https://github.com/CSenshi/nestjs-redis#contributing).
171
+
172
+ ## License
173
+
174
+ MIT © [CSenshi](https://github.com/CSenshi)
package/package.json ADDED
@@ -0,0 +1,81 @@
1
+ {
2
+ "name": "@nestjs-redis/socket.io-adapter",
3
+ "version": "0.0.21",
4
+ "license": "MIT",
5
+ "author": "Saba Pochkhua <saba.pochkhua@gmail.com> (https://github.com/CSenshi)",
6
+ "description": "Redis-powered Socket.IO adapter for NestJS enabling scalable WebSocket connections",
7
+ "keywords": [
8
+ "nestjs",
9
+ "redis",
10
+ "socket.io",
11
+ "websockets",
12
+ "adapter",
13
+ "scalable",
14
+ "typescript",
15
+ "node-redis"
16
+ ],
17
+ "homepage": "https://github.com/CSenshi/nestjs-redis/tree/main/packages/socket.io-adapter",
18
+ "main": "./src/index.js",
19
+ "module": "./src/index.js",
20
+ "types": "./src/index.d.ts",
21
+ "exports": {
22
+ "./package.json": "./package.json",
23
+ ".": {
24
+ "development": "./src/index.ts",
25
+ "types": "./src/index.d.ts",
26
+ "import": "./src/index.js",
27
+ "default": "./src/index.js"
28
+ }
29
+ },
30
+ "files": [
31
+ "src",
32
+ "!**/*.tsbuildinfo"
33
+ ],
34
+ "nx": {
35
+ "name": "socket.io-adapter",
36
+ "targets": {
37
+ "build": {
38
+ "executor": "@nx/js:tsc",
39
+ "outputs": [
40
+ "{options.outputPath}"
41
+ ],
42
+ "options": {
43
+ "outputPath": "dist/packages/socket.io-adapter",
44
+ "tsConfig": "packages/socket.io-adapter/tsconfig.lib.json",
45
+ "packageJson": "packages/socket.io-adapter/package.json",
46
+ "main": "packages/socket.io-adapter/src/index.ts",
47
+ "assets": [
48
+ "packages/socket.io-adapter/*.md",
49
+ "LICENSE"
50
+ ]
51
+ }
52
+ }
53
+ }
54
+ },
55
+ "dependencies": {
56
+ "@nestjs/platform-socket.io": "^11.1.6",
57
+ "@socket.io/redis-adapter": "^8.3.0",
58
+ "tslib": "^2.8.0"
59
+ },
60
+ "devDependencies": {
61
+ "@nestjs-redis/client": "0.10.1",
62
+ "@nestjs/testing": "^11.0.0",
63
+ "@nestjs/websockets": "^11.1.6",
64
+ "redis": "^5.0.0",
65
+ "socket.io-client": "^4.0.0"
66
+ },
67
+ "peerDependencies": {
68
+ "@nestjs/common": "^9.0.0 || ^10.0.0 || ^11.0.0",
69
+ "@nestjs/core": "^9.0.0 || ^10.0.0 || ^11.0.0"
70
+ },
71
+ "engines": {
72
+ "node": ">=18.0.0",
73
+ "npm": ">=8.0.0"
74
+ },
75
+ "repository": {
76
+ "type": "git",
77
+ "url": "https://github.com/CSenshi/nestjs-redis",
78
+ "directory": "packages/socket.io-adapter"
79
+ },
80
+ "type": "commonjs"
81
+ }
package/src/index.d.ts ADDED
@@ -0,0 +1,4 @@
1
+ export * from './lib/exceptions';
2
+ export * from './lib/redis-io.adapter';
3
+ export * from './lib/setup-redis-adapter';
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../packages/socket.io-adapter/src/index.ts"],"names":[],"mappings":"AAAA,cAAc,kBAAkB,CAAC;AACjC,cAAc,wBAAwB,CAAC;AACvC,cAAc,2BAA2B,CAAC"}
package/src/index.js ADDED
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const tslib_1 = require("tslib");
4
+ tslib_1.__exportStar(require("./lib/exceptions"), exports);
5
+ tslib_1.__exportStar(require("./lib/redis-io.adapter"), exports);
6
+ tslib_1.__exportStar(require("./lib/setup-redis-adapter"), exports);
@@ -0,0 +1,7 @@
1
+ export declare class RedisClientNotFoundException extends Error {
2
+ constructor(redisToken?: string);
3
+ }
4
+ export declare class RedisAdapterAlreadySetUpException extends Error {
5
+ constructor();
6
+ }
7
+ //# sourceMappingURL=exceptions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"exceptions.d.ts","sourceRoot":"","sources":["../../../../../packages/socket.io-adapter/src/lib/exceptions.ts"],"names":[],"mappings":"AAAA,qBAAa,4BAA6B,SAAQ,KAAK;gBACzC,UAAU,CAAC,EAAE,MAAM;CA6BhC;AAED,qBAAa,iCAAkC,SAAQ,KAAK;;CAK3D"}
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RedisAdapterAlreadySetUpException = exports.RedisClientNotFoundException = void 0;
4
+ class RedisClientNotFoundException extends Error {
5
+ constructor(redisToken) {
6
+ const baseMessage = `Redis client not found. Please ensure that the Redis client is properly configured and provided in the application context.
7
+
8
+ Most common solution:
9
+ Import and configure RedisModule in your app.module.ts:`;
10
+ const configExample = redisToken
11
+ ? ` RedisModule.forRoot({
12
+ connectionName: '${redisToken}', // <-- connection name
13
+ options: { url: process.env['REDIS_URL'] },
14
+ }),`
15
+ : ` RedisModule.forRoot({
16
+ options: { url: process.env['REDIS_URL'] },
17
+ }),`;
18
+ const troubleshooting = `
19
+
20
+ Make sure you have:
21
+ 1. RedisModule imported in your module`;
22
+ super(baseMessage + '\n\n' + configExample + troubleshooting);
23
+ this.name = 'RedisClientNotFoundException';
24
+ if (redisToken) {
25
+ this.message += `\n\nRequired connection name: ${redisToken}`;
26
+ }
27
+ this.message += `\n`;
28
+ }
29
+ }
30
+ exports.RedisClientNotFoundException = RedisClientNotFoundException;
31
+ class RedisAdapterAlreadySetUpException extends Error {
32
+ constructor() {
33
+ super('Redis adapter is already set up for this application instance.');
34
+ this.name = 'RedisAdapterAlreadySetUpException';
35
+ }
36
+ }
37
+ exports.RedisAdapterAlreadySetUpException = RedisAdapterAlreadySetUpException;
@@ -0,0 +1,11 @@
1
+ import { IoAdapter } from '@nestjs/platform-socket.io';
2
+ import type { RedisClientType } from 'redis';
3
+ export declare class RedisIoAdapter extends IoAdapter {
4
+ private pubClient;
5
+ private subClient;
6
+ private adapterConstructor;
7
+ connectToRedis(redisClient: RedisClientType): Promise<void>;
8
+ createIOServer(port: number, options?: Parameters<IoAdapter['createIOServer']>[1]): ReturnType<IoAdapter['createIOServer']>;
9
+ close(server: Parameters<IoAdapter['close']>[0]): Promise<void>;
10
+ }
11
+ //# sourceMappingURL=redis-io.adapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redis-io.adapter.d.ts","sourceRoot":"","sources":["../../../../../packages/socket.io-adapter/src/lib/redis-io.adapter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAC;AAEvD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,OAAO,CAAC;AAE7C,qBAAa,cAAe,SAAQ,SAAS;IAC3C,OAAO,CAAC,SAAS,CAA8B;IAC/C,OAAO,CAAC,SAAS,CAA8B;IAE/C,OAAO,CAAC,kBAAkB,CAA+C;IAEnE,cAAc,CAAC,WAAW,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC;IASxD,cAAc,CACrB,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE,UAAU,CAAC,SAAS,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAC,GACnD,UAAU,CAAC,SAAS,CAAC,gBAAgB,CAAC,CAAC;IAM3B,KAAK,CAClB,MAAM,EAAE,UAAU,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,GACxC,OAAO,CAAC,IAAI,CAAC;CAOjB"}
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RedisIoAdapter = void 0;
4
+ const platform_socket_io_1 = require("@nestjs/platform-socket.io");
5
+ const redis_adapter_1 = require("@socket.io/redis-adapter");
6
+ class RedisIoAdapter extends platform_socket_io_1.IoAdapter {
7
+ async connectToRedis(redisClient) {
8
+ this.pubClient = redisClient;
9
+ this.subClient = this.pubClient.duplicate();
10
+ await this.subClient.connect();
11
+ this.adapterConstructor = (0, redis_adapter_1.createAdapter)(this.pubClient, this.subClient);
12
+ }
13
+ createIOServer(port, options) {
14
+ const server = super.createIOServer(port, options);
15
+ server.adapter(this.adapterConstructor);
16
+ return server;
17
+ }
18
+ async close(server) {
19
+ super.close(server);
20
+ if (this.subClient) {
21
+ await this.subClient.quit();
22
+ }
23
+ }
24
+ }
25
+ exports.RedisIoAdapter = RedisIoAdapter;
@@ -0,0 +1,11 @@
1
+ import type { INestApplication } from '@nestjs/common';
2
+ /**
3
+ * Sets up the Redis adapter for a NestJS application.
4
+ *
5
+ * @param app - The NestJS application instance
6
+ * @param redisToken - Optional Redis client token for named connections
7
+ * @returns A promise that resolves when the adapter is set up
8
+ * @throws RedisClientNotFoundException if the Redis client is not found
9
+ */
10
+ export declare function setupRedisAdapter(app: INestApplication, redisToken?: string): Promise<void>;
11
+ //# sourceMappingURL=setup-redis-adapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"setup-redis-adapter.d.ts","sourceRoot":"","sources":["../../../../../packages/socket.io-adapter/src/lib/setup-redis-adapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAWvD;;;;;;;GAOG;AACH,wBAAsB,iBAAiB,CACrC,GAAG,EAAE,gBAAgB,EACrB,UAAU,CAAC,EAAE,MAAM,GAClB,OAAO,CAAC,IAAI,CAAC,CAsBf"}
@@ -0,0 +1,50 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.setupRedisAdapter = setupRedisAdapter;
4
+ const core_1 = require("@nestjs/core");
5
+ const exceptions_1 = require("@nestjs/core/errors/exceptions");
6
+ const exceptions_2 = require("./exceptions");
7
+ const redis_io_adapter_1 = require("./redis-io.adapter");
8
+ const appSet = new Set();
9
+ /**
10
+ * Sets up the Redis adapter for a NestJS application.
11
+ *
12
+ * @param app - The NestJS application instance
13
+ * @param redisToken - Optional Redis client token for named connections
14
+ * @returns A promise that resolves when the adapter is set up
15
+ * @throws RedisClientNotFoundException if the Redis client is not found
16
+ */
17
+ async function setupRedisAdapter(app, redisToken) {
18
+ if (appSet.has(app)) {
19
+ throw new exceptions_2.RedisAdapterAlreadySetUpException();
20
+ }
21
+ appSet.add(app);
22
+ const redisIoAdapter = new redis_io_adapter_1.RedisIoAdapter(app);
23
+ try {
24
+ const moduleRef = app.get(core_1.ModuleRef);
25
+ const redisClient = moduleRef.get(RedisToken(redisToken), {
26
+ strict: false,
27
+ });
28
+ await redisIoAdapter.connectToRedis(redisClient);
29
+ app.useWebSocketAdapter(redisIoAdapter);
30
+ }
31
+ catch (error) {
32
+ if (error instanceof exceptions_1.UnknownElementException) {
33
+ throw new exceptions_2.RedisClientNotFoundException(redisToken);
34
+ }
35
+ throw error;
36
+ }
37
+ }
38
+ /**
39
+ * Creates a Redis client injection token.
40
+ *
41
+ * @param connectionName - Optional connection name
42
+ * @returns Injection token for the Redis client
43
+ * @publicApi
44
+ */
45
+ function RedisToken(connectionName) {
46
+ if (connectionName) {
47
+ return `REDIS_CLIENT_${connectionName.toUpperCase()}`;
48
+ }
49
+ return 'REDIS_CLIENT';
50
+ }