@venizia/ignis-docs 0.0.5 → 0.0.6-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.
Files changed (98) hide show
  1. package/package.json +1 -1
  2. package/wiki/best-practices/architecture-decisions.md +0 -8
  3. package/wiki/best-practices/code-style-standards/control-flow.md +1 -1
  4. package/wiki/best-practices/performance-optimization.md +3 -3
  5. package/wiki/best-practices/security-guidelines.md +2 -2
  6. package/wiki/best-practices/troubleshooting-tips.md +1 -1
  7. package/wiki/guides/core-concepts/components-guide.md +1 -1
  8. package/wiki/guides/core-concepts/components.md +2 -2
  9. package/wiki/guides/core-concepts/dependency-injection.md +1 -1
  10. package/wiki/guides/core-concepts/services.md +1 -1
  11. package/wiki/guides/tutorials/building-a-crud-api.md +1 -1
  12. package/wiki/guides/tutorials/ecommerce-api.md +2 -2
  13. package/wiki/guides/tutorials/realtime-chat.md +6 -6
  14. package/wiki/guides/tutorials/testing.md +1 -1
  15. package/wiki/references/base/bootstrapping.md +0 -2
  16. package/wiki/references/base/components.md +2 -2
  17. package/wiki/references/base/controllers.md +0 -1
  18. package/wiki/references/base/datasources.md +1 -1
  19. package/wiki/references/base/dependency-injection.md +1 -1
  20. package/wiki/references/base/filter-system/quick-reference.md +0 -14
  21. package/wiki/references/base/middlewares.md +0 -8
  22. package/wiki/references/base/providers.md +0 -9
  23. package/wiki/references/base/services.md +0 -1
  24. package/wiki/references/components/authentication/api.md +444 -0
  25. package/wiki/references/components/authentication/errors.md +177 -0
  26. package/wiki/references/components/authentication/index.md +571 -0
  27. package/wiki/references/components/authentication/usage.md +781 -0
  28. package/wiki/references/components/health-check.md +292 -103
  29. package/wiki/references/components/index.md +14 -12
  30. package/wiki/references/components/mail/api.md +505 -0
  31. package/wiki/references/components/mail/errors.md +176 -0
  32. package/wiki/references/components/mail/index.md +535 -0
  33. package/wiki/references/components/mail/usage.md +404 -0
  34. package/wiki/references/components/request-tracker.md +229 -25
  35. package/wiki/references/components/socket-io/api.md +1051 -0
  36. package/wiki/references/components/socket-io/errors.md +119 -0
  37. package/wiki/references/components/socket-io/index.md +410 -0
  38. package/wiki/references/components/socket-io/usage.md +322 -0
  39. package/wiki/references/components/static-asset/api.md +261 -0
  40. package/wiki/references/components/static-asset/errors.md +89 -0
  41. package/wiki/references/components/static-asset/index.md +617 -0
  42. package/wiki/references/components/static-asset/usage.md +364 -0
  43. package/wiki/references/components/swagger.md +390 -110
  44. package/wiki/references/components/template/api-page.md +125 -0
  45. package/wiki/references/components/template/errors-page.md +100 -0
  46. package/wiki/references/components/template/index.md +104 -0
  47. package/wiki/references/components/template/setup-page.md +134 -0
  48. package/wiki/references/components/template/single-page.md +132 -0
  49. package/wiki/references/components/template/usage-page.md +127 -0
  50. package/wiki/references/components/websocket/api.md +508 -0
  51. package/wiki/references/components/websocket/errors.md +123 -0
  52. package/wiki/references/components/websocket/index.md +453 -0
  53. package/wiki/references/components/websocket/usage.md +475 -0
  54. package/wiki/references/helpers/cron/index.md +224 -0
  55. package/wiki/references/helpers/crypto/index.md +537 -0
  56. package/wiki/references/helpers/env/index.md +214 -0
  57. package/wiki/references/helpers/error/index.md +232 -0
  58. package/wiki/references/helpers/index.md +16 -15
  59. package/wiki/references/helpers/inversion/index.md +608 -0
  60. package/wiki/references/helpers/logger/index.md +600 -0
  61. package/wiki/references/helpers/network/api.md +986 -0
  62. package/wiki/references/helpers/network/index.md +620 -0
  63. package/wiki/references/helpers/queue/index.md +589 -0
  64. package/wiki/references/helpers/redis/index.md +495 -0
  65. package/wiki/references/helpers/socket-io/api.md +497 -0
  66. package/wiki/references/helpers/socket-io/index.md +513 -0
  67. package/wiki/references/helpers/storage/api.md +705 -0
  68. package/wiki/references/helpers/storage/index.md +583 -0
  69. package/wiki/references/helpers/template/index.md +66 -0
  70. package/wiki/references/helpers/template/single-page.md +126 -0
  71. package/wiki/references/helpers/testing/index.md +510 -0
  72. package/wiki/references/helpers/types/index.md +512 -0
  73. package/wiki/references/helpers/uid/index.md +272 -0
  74. package/wiki/references/helpers/websocket/api.md +736 -0
  75. package/wiki/references/helpers/websocket/index.md +574 -0
  76. package/wiki/references/helpers/worker-thread/index.md +470 -0
  77. package/wiki/references/quick-reference.md +3 -18
  78. package/wiki/references/utilities/jsx.md +1 -8
  79. package/wiki/references/utilities/statuses.md +0 -7
  80. package/wiki/references/components/authentication.md +0 -476
  81. package/wiki/references/components/mail.md +0 -687
  82. package/wiki/references/components/socket-io.md +0 -562
  83. package/wiki/references/components/static-asset.md +0 -1277
  84. package/wiki/references/helpers/cron.md +0 -108
  85. package/wiki/references/helpers/crypto.md +0 -132
  86. package/wiki/references/helpers/env.md +0 -83
  87. package/wiki/references/helpers/error.md +0 -97
  88. package/wiki/references/helpers/inversion.md +0 -176
  89. package/wiki/references/helpers/logger.md +0 -296
  90. package/wiki/references/helpers/network.md +0 -396
  91. package/wiki/references/helpers/queue.md +0 -150
  92. package/wiki/references/helpers/redis.md +0 -142
  93. package/wiki/references/helpers/socket-io.md +0 -932
  94. package/wiki/references/helpers/storage.md +0 -665
  95. package/wiki/references/helpers/testing.md +0 -133
  96. package/wiki/references/helpers/types.md +0 -167
  97. package/wiki/references/helpers/uid.md +0 -167
  98. package/wiki/references/helpers/worker-thread.md +0 -178
@@ -0,0 +1,495 @@
1
+ # Redis
2
+
3
+ Powerful Redis abstraction supporting single instances and clusters via `ioredis`, with automatic JSON serialization, connection lifecycle callbacks, Pub/Sub with optional zlib compression, RedisJSON operations, and raw command execution.
4
+
5
+ ## Quick Reference
6
+
7
+ | Class | Extends | Use Case |
8
+ |-------|---------|----------|
9
+ | **`DefaultRedisHelper`** | `BaseHelper` | Base class with unified API for all Redis operations |
10
+ | **`RedisHelper`** | `DefaultRedisHelper` | Single Redis instance with auto-connect and retry strategy |
11
+ | **`RedisClusterHelper`** | `DefaultRedisHelper` | Redis cluster with multi-node support |
12
+
13
+ #### Import Paths
14
+
15
+ ```typescript
16
+ // From the helpers package
17
+ import {
18
+ DefaultRedisHelper,
19
+ RedisHelper,
20
+ RedisClusterHelper,
21
+ } from '@venizia/ignis-helpers';
22
+
23
+ // Or from the core package (re-exports everything)
24
+ import {
25
+ DefaultRedisHelper,
26
+ RedisHelper,
27
+ RedisClusterHelper,
28
+ } from '@venizia/ignis';
29
+
30
+ // Types
31
+ import type {
32
+ IRedisHelperOptions,
33
+ IRedisClusterHelperOptions,
34
+ IRedisHelperCallbacks,
35
+ IRedisHelperProps,
36
+ IRedisClusterHelperProps,
37
+ } from '@venizia/ignis-helpers';
38
+ ```
39
+
40
+ ## Creating an Instance
41
+
42
+ ### Single Instance
43
+
44
+ Use `RedisHelper` for connecting to a single Redis server.
45
+
46
+ ```typescript
47
+ import { RedisHelper } from '@venizia/ignis-helpers';
48
+
49
+ const redis = new RedisHelper({
50
+ name: 'my-redis',
51
+ host: 'localhost',
52
+ port: 6379,
53
+ password: 'secret',
54
+ database: 0,
55
+ autoConnect: true,
56
+ maxRetry: 5,
57
+
58
+ onInitialized: ({ name, helper }) => {
59
+ console.log(`Redis "${name}" initialized`);
60
+ },
61
+ onConnected: ({ name }) => {
62
+ console.log(`Redis "${name}" connected`);
63
+ },
64
+ onReady: ({ name }) => {
65
+ console.log(`Redis "${name}" ready`);
66
+ },
67
+ onError: ({ name, error }) => {
68
+ console.error(`Redis "${name}" error:`, error);
69
+ },
70
+ });
71
+ ```
72
+
73
+ #### `IRedisHelperOptions`
74
+
75
+ | Option | Type | Required | Default | Description |
76
+ |--------|------|----------|---------|-------------|
77
+ | `name` | `string` | Yes | -- | Unique identifier for this client instance |
78
+ | `host` | `string` | Yes | -- | Redis server hostname |
79
+ | `port` | `string \| number` | Yes | -- | Redis server port |
80
+ | `password` | `string` | Yes | -- | Redis password |
81
+ | `user` | `string` | No | -- | Redis username (ACL authentication) |
82
+ | `database` | `number` | No | `0` | Redis database index |
83
+ | `autoConnect` | `boolean` | No | `true` | Connect immediately on creation. When `false`, uses ioredis `lazyConnect` mode |
84
+ | `maxRetry` | `number` | No | `0` | Maximum reconnection attempts. `0` = unlimited retries. Values below `0` disable retry entirely |
85
+ | `onInitialized` | `(opts: { name: string; helper: DefaultRedisHelper }) => void` | No | -- | Called synchronously immediately after client construction |
86
+ | `onConnected` | `(opts: { name: string; helper: DefaultRedisHelper }) => void` | No | -- | Called when the TCP connection is established |
87
+ | `onReady` | `(opts: { name: string; helper: DefaultRedisHelper }) => void` | No | -- | Called when the client is ready to accept commands |
88
+ | `onError` | `(opts: { name: string; helper: DefaultRedisHelper; error: any }) => void` | No | -- | Called on connection or command errors |
89
+
90
+ **Retry strategy:** Exponential backoff clamped between 1s and 5s: `Math.max(Math.min(attempt * 2000, 5000), 1000)`. The ioredis option `maxRetriesPerRequest` is set to `null` internally, which is required for compatibility with BullMQ.
91
+
92
+ **ioredis configuration:** `RedisHelper` creates an ioredis `Redis` instance with `showFriendlyErrorStack: true` for better error diagnostics.
93
+
94
+ ### Cluster
95
+
96
+ Use `RedisClusterHelper` for connecting to a Redis cluster.
97
+
98
+ ```typescript
99
+ import { RedisClusterHelper } from '@venizia/ignis-helpers';
100
+
101
+ const cluster = new RedisClusterHelper({
102
+ name: 'my-cluster',
103
+ nodes: [
104
+ { host: 'redis-node-1', port: 7000 },
105
+ { host: 'redis-node-2', port: 7001, password: 'node-specific-pass' },
106
+ { host: 'redis-node-3', port: 7002 },
107
+ ],
108
+ clusterOptions: {
109
+ redisOptions: { password: 'cluster-password' },
110
+ },
111
+
112
+ onReady: ({ name }) => {
113
+ console.log(`Cluster "${name}" ready`);
114
+ },
115
+ });
116
+ ```
117
+
118
+ #### `IRedisClusterHelperOptions`
119
+
120
+ | Option | Type | Required | Default | Description |
121
+ |--------|------|----------|---------|-------------|
122
+ | `name` | `string` | Yes | -- | Unique identifier for this cluster client |
123
+ | `nodes` | `Array<{ host: string; port: string \| number; password?: string }>` | Yes | -- | List of cluster node addresses |
124
+ | `clusterOptions` | `ClusterOptions` | No | -- | ioredis `ClusterOptions` passed directly to the `Cluster` constructor |
125
+ | `onInitialized` | `(opts: { name: string; helper: DefaultRedisHelper }) => void` | No | -- | Called synchronously immediately after cluster client construction |
126
+ | `onConnected` | `(opts: { name: string; helper: DefaultRedisHelper }) => void` | No | -- | Called when connected |
127
+ | `onReady` | `(opts: { name: string; helper: DefaultRedisHelper }) => void` | No | -- | Called when ready |
128
+ | `onError` | `(opts: { name: string; helper: DefaultRedisHelper; error: any }) => void` | No | -- | Called on errors |
129
+
130
+ ### Lifecycle Callbacks
131
+
132
+ All Redis helpers accept the same lifecycle callbacks via `IRedisHelperCallbacks`:
133
+
134
+ ```typescript
135
+ interface IRedisHelperCallbacks {
136
+ onInitialized?: (opts: { name: string; helper: DefaultRedisHelper }) => void;
137
+ onConnected?: (opts: { name: string; helper: DefaultRedisHelper }) => void;
138
+ onReady?: (opts: { name: string; helper: DefaultRedisHelper }) => void;
139
+ onError?: (opts: { name: string; helper: DefaultRedisHelper; error: any }) => void;
140
+ }
141
+ ```
142
+
143
+ | Callback | Redis Event | When |
144
+ |----------|-------------|------|
145
+ | `onInitialized` | -- | Immediately after client construction (synchronous) |
146
+ | `onConnected` | `connect` | TCP connection established |
147
+ | `onReady` | `ready` | Client ready to accept commands |
148
+ | `onError` | `error` | Connection or command error |
149
+
150
+ Additionally, the client internally logs a warning on `reconnecting` events.
151
+
152
+ ## Usage
153
+
154
+ All operations below are available on both `RedisHelper` and `RedisClusterHelper` via the shared `DefaultRedisHelper` base class.
155
+
156
+ ### Connection Management
157
+
158
+ ```typescript
159
+ // Manual connect (when autoConnect is false)
160
+ const connected = await redis.connect();
161
+ // => true if status becomes 'ready', false if already connected/connecting
162
+
163
+ // Disconnect gracefully (sends QUIT command)
164
+ const disconnected = await redis.disconnect();
165
+ // => true if quit succeeded
166
+
167
+ // Health check
168
+ const pong = await redis.ping();
169
+ // => 'PONG'
170
+
171
+ // Access underlying ioredis client
172
+ const ioredisClient = redis.getClient();
173
+ // RedisHelper returns Redis, RedisClusterHelper returns Cluster
174
+ ```
175
+
176
+ > [!NOTE]
177
+ > `connect()` resolves to `false` without action if the client status is already `ready`, `reconnecting`, or `connecting`. Similarly, `disconnect()` resolves to `false` if the status is `end` or `close`.
178
+
179
+ ### Key-Value Operations
180
+
181
+ ```typescript
182
+ // Set a value (auto-serialized to JSON)
183
+ await redis.set({ key: 'user:1', value: { name: 'Alice', age: 30 } });
184
+
185
+ // Set with logging enabled
186
+ await redis.set({ key: 'user:1', value: { name: 'Alice' }, options: { log: true } });
187
+
188
+ // Get raw string value
189
+ const raw = await redis.get({ key: 'user:1' });
190
+ // => '{"name":"Alice","age":30}'
191
+
192
+ // Get with custom transform
193
+ const parsed = await redis.get({
194
+ key: 'user:1',
195
+ transform: (input) => JSON.parse(input),
196
+ });
197
+
198
+ // Convenience: get as string (alias for get)
199
+ const str = await redis.getString({ key: 'user:1' });
200
+
201
+ // Convenience: get as parsed JSON object
202
+ const user = await redis.getObject({ key: 'user:1' });
203
+ // => { name: 'Alice', age: 30 }
204
+
205
+ // Delete keys
206
+ await redis.del({ keys: ['user:1', 'user:2'] });
207
+ ```
208
+
209
+ ### Multi-Key Operations
210
+
211
+ ```typescript
212
+ // Set multiple keys at once
213
+ await redis.mset({
214
+ payload: [
215
+ { key: 'user:1', value: { name: 'Alice' } },
216
+ { key: 'user:2', value: { name: 'Bob' } },
217
+ ],
218
+ });
219
+
220
+ // Get multiple raw values
221
+ const values = await redis.mget({ keys: ['user:1', 'user:2'] });
222
+ // => ['{"name":"Alice"}', '{"name":"Bob"}']
223
+
224
+ // Get multiple with transform
225
+ const users = await redis.mget({
226
+ keys: ['user:1', 'user:2'],
227
+ transform: (el) => JSON.parse(el),
228
+ });
229
+
230
+ // Convenience: get multiple strings
231
+ const strings = await redis.getStrings({ keys: ['key1', 'key2'] });
232
+
233
+ // Convenience: get multiple parsed objects
234
+ const objects = await redis.getObjects({ keys: ['user:1', 'user:2'] });
235
+ ```
236
+
237
+ > [!TIP]
238
+ > `mSet()`, `mGet()`, `hSet()`, and `hGetAll()` are camelCase aliases for `mset()`, `mget()`, `hset()`, and `hgetall()` respectively. Both forms are valid.
239
+
240
+ ### Hash Operations
241
+
242
+ ```typescript
243
+ // Set hash fields
244
+ await redis.hset({
245
+ key: 'session:abc',
246
+ value: { userId: 'u1', token: 'tok123', createdAt: '2025-01-01' },
247
+ });
248
+
249
+ // Set hash fields with logging
250
+ await redis.hset({
251
+ key: 'session:abc',
252
+ value: { userId: 'u1' },
253
+ options: { log: true },
254
+ });
255
+
256
+ // Get all hash fields
257
+ const session = await redis.hgetall({ key: 'session:abc' });
258
+ // => { userId: 'u1', token: 'tok123', createdAt: '2025-01-01' }
259
+
260
+ // Get all hash fields with transform
261
+ const transformed = await redis.hgetall({
262
+ key: 'session:abc',
263
+ transform: (input) => ({ ...input, extra: true }),
264
+ });
265
+ ```
266
+
267
+ ### Key Scanning
268
+
269
+ ```typescript
270
+ // Find keys matching a pattern
271
+ const matchingKeys = await redis.keys({ key: 'user:*' });
272
+ // => ['user:1', 'user:2', ...]
273
+ ```
274
+
275
+ > [!WARNING]
276
+ > `keys()` uses the Redis `KEYS` command, which scans the entire keyspace. Avoid using it in production on large databases -- prefer `SCAN` via `execute()` instead.
277
+
278
+ ### RedisJSON Operations
279
+
280
+ If your Redis server has the [RedisJSON](https://redis.io/docs/stack/json/) module installed, you can use the `j*` methods for native JSON document operations.
281
+
282
+ ```typescript
283
+ // Set a JSON document
284
+ await redis.jSet({ key: 'doc:1', path: '$', value: { name: 'Alice', scores: [10, 20] } });
285
+
286
+ // Get entire document (path defaults to '$')
287
+ const doc = await redis.jGet({ key: 'doc:1' });
288
+
289
+ // Get a nested path
290
+ const scores = await redis.jGet({ key: 'doc:1', path: '$.scores' });
291
+
292
+ // Delete a JSON path
293
+ await redis.jDelete({ key: 'doc:1', path: '$.scores' });
294
+
295
+ // Delete entire document (path defaults to '$')
296
+ await redis.jDelete({ key: 'doc:1' });
297
+
298
+ // Increment a number at a path
299
+ await redis.jNumberIncreaseBy({ key: 'doc:1', path: '$.counter', value: 5 });
300
+
301
+ // Append to a string at a path
302
+ await redis.jStringAppend({ key: 'doc:1', path: '$.name', value: ' Smith' });
303
+
304
+ // Push to an array at a path
305
+ await redis.jPush({ key: 'doc:1', path: '$.tags', value: 'new-tag' });
306
+
307
+ // Pop from an array at a path
308
+ const popped = await redis.jPop({ key: 'doc:1', path: '$.tags' });
309
+ ```
310
+
311
+ #### RedisJSON Method Signatures
312
+
313
+ | Method | Parameters | Returns | Redis Command |
314
+ |--------|-----------|---------|---------------|
315
+ | `jSet<T>()` | `{ key: string; path: string; value: T }` | `Promise<string \| null>` | `JSON.SET` |
316
+ | `jGet<T>()` | `{ key: string; path?: string }` | `Promise<T \| null>` | `JSON.GET` |
317
+ | `jDelete()` | `{ key: string; path?: string }` | `Promise<number>` | `JSON.DEL` |
318
+ | `jNumberIncreaseBy()` | `{ key: string; path: string; value: number }` | `Promise<string \| null>` | `JSON.NUMINCRBY` |
319
+ | `jStringAppend()` | `{ key: string; path: string; value: string }` | `Promise<number[] \| null>` | `JSON.STRAPPEND` |
320
+ | `jPush<T>()` | `{ key: string; path: string; value: T }` | `Promise<number[] \| null>` | `JSON.ARRAPPEND` |
321
+ | `jPop<T>()` | `{ key: string; path: string }` | `Promise<T \| null>` | `JSON.ARRPOP` |
322
+
323
+ `jGet` and `jDelete` default `path` to `'$'` (root) when omitted. All other `j*` methods require `path` explicitly.
324
+
325
+ ### Pub/Sub
326
+
327
+ The helper supports Redis Pub/Sub for real-time messaging with optional zlib compression.
328
+
329
+ ```typescript
330
+ // Subscribe to a topic
331
+ redis.subscribe({ topic: 'events' });
332
+
333
+ // Listen for messages on the underlying ioredis client
334
+ redis.getClient().on('message', (channel, message) => {
335
+ if (channel === 'events') {
336
+ console.log('Received:', JSON.parse(message));
337
+ }
338
+ });
339
+
340
+ // Publish to one or more topics
341
+ await redis.publish({
342
+ topics: ['events', 'audit-log'],
343
+ payload: { action: 'user.created', userId: 'u1' },
344
+ });
345
+
346
+ // Publish with zlib compression
347
+ await redis.publish({
348
+ topics: ['compressed-channel'],
349
+ payload: { large: 'dataset' },
350
+ useCompress: true,
351
+ });
352
+
353
+ // Unsubscribe from a topic
354
+ redis.unsubscribe({ topic: 'events' });
355
+ ```
356
+
357
+ > [!IMPORTANT]
358
+ > When using Pub/Sub, the subscribing client enters subscriber mode and can only execute `SUBSCRIBE`, `PSUBSCRIBE`, `UNSUBSCRIBE`, `PUNSUBSCRIBE`, `PING`, and `QUIT` commands. Use a separate `RedisHelper` instance for Pub/Sub if you also need to perform regular data operations.
359
+
360
+ ### Raw Command Execution
361
+
362
+ For commands not wrapped by the helper, use `execute()` to call any Redis command directly.
363
+
364
+ ```typescript
365
+ // Execute any Redis command
366
+ const result = await redis.execute<string>('SET', ['mykey', 'myvalue', 'EX', 60]);
367
+
368
+ // Command without parameters
369
+ const info = await redis.execute<string>('INFO');
370
+
371
+ // SCAN instead of KEYS for production use
372
+ const [cursor, keys] = await redis.execute<[string, string[]]>(
373
+ 'SCAN', [0, 'MATCH', 'user:*', 'COUNT', 100],
374
+ );
375
+ ```
376
+
377
+ ## API Summary
378
+
379
+ | Method | Returns | Description |
380
+ |--------|---------|-------------|
381
+ | **Connection** | | |
382
+ | `connect()` | `Promise<boolean>` | Manual connect (no-op if already connected/connecting/ready) |
383
+ | `disconnect()` | `Promise<boolean>` | Graceful disconnect via `QUIT` (no-op if already ended/closed) |
384
+ | `ping()` | `Promise<string>` | Health check, returns `'PONG'` |
385
+ | `getClient()` | `Redis \| Cluster` | Access the underlying ioredis client |
386
+ | **Key-Value** | | |
387
+ | `set<T>(opts)` | `Promise<void>` | Set a key with JSON-serialized value. Options: `{ key, value, options?: { log } }` |
388
+ | `get<T>(opts)` | `Promise<T \| null>` | Get raw value with optional transform. Options: `{ key, transform? }` |
389
+ | `getString(opts)` | `Promise<string \| null>` | Get raw string value. Options: `{ key }` |
390
+ | `getObject(opts)` | `Promise<object \| null>` | Get value parsed as JSON. Options: `{ key }` |
391
+ | `del(opts)` | `Promise<number>` | Delete one or more keys. Options: `{ keys: string[] }` |
392
+ | **Multi-Key** | | |
393
+ | `mset<T>(opts)` / `mSet<T>(opts)` | `Promise<void>` | Set multiple key-value pairs. Options: `{ payload: Array<{ key, value }>, options?: { log } }` |
394
+ | `mget<T>(opts)` / `mGet<T>(opts)` | `Promise<(T \| null)[]>` | Get multiple values with optional transform. Options: `{ keys, transform? }` |
395
+ | `getStrings(opts)` | `Promise<(string \| null)[]>` | Get multiple raw string values. Options: `{ keys }` |
396
+ | `getObjects(opts)` | `Promise<(object \| null)[]>` | Get multiple values parsed as JSON. Options: `{ keys }` |
397
+ | **Hashes** | | |
398
+ | `hset<T>(opts)` / `hSet<T>(opts)` | `Promise<number>` | Set hash fields. Options: `{ key, value: Record<string, unknown>, options?: { log } }` |
399
+ | `hgetall(opts)` / `hGetAll(opts)` | `Promise<Record<string, string> \| null>` | Get all hash fields with optional transform. Options: `{ key, transform? }` |
400
+ | **Key Scanning** | | |
401
+ | `keys(opts)` | `Promise<string[]>` | Find keys matching a glob pattern. Options: `{ key }` |
402
+ | **RedisJSON** | | |
403
+ | `jSet<T>(opts)` | `Promise<string \| null>` | Set a JSON document at path (`JSON.SET`) |
404
+ | `jGet<T>(opts)` | `Promise<T \| null>` | Get a JSON document or path (`JSON.GET`) |
405
+ | `jDelete(opts)` | `Promise<number>` | Delete a JSON path (`JSON.DEL`) |
406
+ | `jNumberIncreaseBy(opts)` | `Promise<string \| null>` | Increment a number at path (`JSON.NUMINCRBY`) |
407
+ | `jStringAppend(opts)` | `Promise<number[] \| null>` | Append to a string at path (`JSON.STRAPPEND`) |
408
+ | `jPush<T>(opts)` | `Promise<number[] \| null>` | Push to an array at path (`JSON.ARRAPPEND`) |
409
+ | `jPop<T>(opts)` | `Promise<T \| null>` | Pop from an array at path (`JSON.ARRPOP`) |
410
+ | **Pub/Sub** | | |
411
+ | `subscribe(opts)` | `void` | Subscribe to a topic. Options: `{ topic }` |
412
+ | `publish<T>(opts)` | `Promise<void>` | Publish to one or more topics with optional compression. Options: `{ topics, payload, useCompress? }` |
413
+ | `unsubscribe(opts)` | `void` | Unsubscribe from a topic. Options: `{ topic }` |
414
+ | **Raw** | | |
415
+ | `execute<R>(command, parameters?)` | `Promise<R>` | Execute any Redis command directly |
416
+
417
+ ## Troubleshooting
418
+
419
+ ### "[execute] Invalid client to execute | command: ..."
420
+
421
+ **Cause:** `execute()` was called when the ioredis client is `null` or `undefined`. This typically happens if the helper was not properly constructed or the connection was never established.
422
+
423
+ **Fix:** Ensure the helper is instantiated correctly and, if using `autoConnect: false`, call `await redis.connect()` before issuing commands.
424
+
425
+ ### "[subscribe] Failed to subscribe to topic: ..."
426
+
427
+ **Cause:** The ioredis `subscribe()` callback received an error. This can happen if the client lost its connection or the Redis server rejected the subscription.
428
+
429
+ **Fix:** Check that the Redis server is reachable and the client is in a valid state. Monitor the `onError` callback for connection issues.
430
+
431
+ ### "[unsubscribe] Failed to unsubscribe from topic: ..."
432
+
433
+ **Cause:** The ioredis `unsubscribe()` callback received an error, usually due to a broken connection.
434
+
435
+ **Fix:** Same as above -- verify connectivity and client state.
436
+
437
+ ### Connection Refused / Timeout
438
+
439
+ **Symptoms:** `ECONNREFUSED`, connection hangs, or `onError` fires immediately.
440
+
441
+ **Checklist:**
442
+ - Verify Redis is running and reachable at the configured `host:port`
443
+ - Check firewall rules and network access between your application and the Redis server
444
+ - If using `autoConnect: false`, ensure you call `await redis.connect()` before any operations
445
+ - Verify `password` is correct (Redis returns a generic error for auth failure)
446
+ - For clusters, ensure all node addresses are reachable and the cluster is healthy (`redis-cli cluster info`)
447
+
448
+ ### Pub/Sub Subscriber Mode Conflicts
449
+
450
+ **Symptoms:** `ERR only (P|S)SUBSCRIBE / (P|S)UNSUBSCRIBE / PING / QUIT / RESET allowed in this context`
451
+
452
+ **Cause:** You called `subscribe()` on a client and then attempted a regular command (`get`, `set`, etc.) on the same client.
453
+
454
+ **Fix:** Use a separate connection for Pub/Sub:
455
+
456
+ ```typescript
457
+ const dataClient = new RedisHelper({ name: 'data', host, port, password });
458
+ const subClient = new RedisHelper({ name: 'sub', host, port, password });
459
+
460
+ // Use subClient only for subscribe/unsubscribe
461
+ subClient.subscribe({ topic: 'events' });
462
+ subClient.getClient().on('message', (channel, msg) => { /* ... */ });
463
+
464
+ // Use dataClient for everything else
465
+ await dataClient.set({ key: 'foo', value: 'bar' });
466
+ ```
467
+
468
+ ### RedisJSON Commands Return Errors
469
+
470
+ **Symptoms:** `ERR unknown command 'JSON.SET'`
471
+
472
+ **Cause:** The RedisJSON module is not installed on your Redis server.
473
+
474
+ **Fix:** Install Redis Stack or the RedisJSON module. See [RedisJSON documentation](https://redis.io/docs/stack/json/).
475
+
476
+ ## See Also
477
+
478
+ - **Related Concepts:**
479
+ - [Services](/guides/core-concepts/services) - Using Redis in services
480
+
481
+ - **Other Helpers:**
482
+ - [Helpers Index](../index) - All available helpers
483
+ - [Queue Helper](../queue/) - BullMQ uses Redis as backend
484
+
485
+ - **References:**
486
+ - [DataSources](/references/base/datasources) - Database connections
487
+
488
+ - **External Resources:**
489
+ - [ioredis Documentation](https://github.com/redis/ioredis) - Redis client library
490
+ - [Redis Commands](https://redis.io/commands/) - Redis command reference
491
+ - [RedisJSON](https://redis.io/docs/stack/json/) - JSON module documentation
492
+
493
+ - **Best Practices:**
494
+ - [Performance Optimization](/best-practices/performance-optimization) - Caching strategies
495
+ - [Security Guidelines](/best-practices/security-guidelines) - Redis security