chub-dev 0.1.0 → 0.1.2-beta.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 (139) hide show
  1. package/README.md +55 -0
  2. package/bin/chub-mcp +2 -0
  3. package/dist/airtable/docs/database/javascript/DOC.md +1437 -0
  4. package/dist/airtable/docs/database/python/DOC.md +1735 -0
  5. package/dist/amplitude/docs/analytics/javascript/DOC.md +1282 -0
  6. package/dist/amplitude/docs/analytics/python/DOC.md +1199 -0
  7. package/dist/anthropic/docs/claude-api/javascript/DOC.md +503 -0
  8. package/dist/anthropic/docs/claude-api/python/DOC.md +389 -0
  9. package/dist/asana/docs/tasks/DOC.md +1396 -0
  10. package/dist/assemblyai/docs/transcription/DOC.md +1043 -0
  11. package/dist/atlassian/docs/confluence/javascript/DOC.md +1347 -0
  12. package/dist/atlassian/docs/confluence/python/DOC.md +1604 -0
  13. package/dist/auth0/docs/identity/javascript/DOC.md +968 -0
  14. package/dist/auth0/docs/identity/python/DOC.md +1199 -0
  15. package/dist/aws/docs/s3/javascript/DOC.md +1773 -0
  16. package/dist/aws/docs/s3/python/DOC.md +1807 -0
  17. package/dist/binance/docs/trading/javascript/DOC.md +1315 -0
  18. package/dist/binance/docs/trading/python/DOC.md +1454 -0
  19. package/dist/braintree/docs/gateway/javascript/DOC.md +1278 -0
  20. package/dist/braintree/docs/gateway/python/DOC.md +1179 -0
  21. package/dist/chromadb/docs/embeddings-db/javascript/DOC.md +1263 -0
  22. package/dist/chromadb/docs/embeddings-db/python/DOC.md +1707 -0
  23. package/dist/clerk/docs/auth/javascript/DOC.md +1220 -0
  24. package/dist/clerk/docs/auth/python/DOC.md +274 -0
  25. package/dist/cloudflare/docs/workers/javascript/DOC.md +918 -0
  26. package/dist/cloudflare/docs/workers/python/DOC.md +994 -0
  27. package/dist/cockroachdb/docs/distributed-db/DOC.md +1500 -0
  28. package/dist/cohere/docs/llm/DOC.md +1335 -0
  29. package/dist/datadog/docs/monitoring/javascript/DOC.md +1740 -0
  30. package/dist/datadog/docs/monitoring/python/DOC.md +1815 -0
  31. package/dist/deepgram/docs/speech/javascript/DOC.md +885 -0
  32. package/dist/deepgram/docs/speech/python/DOC.md +685 -0
  33. package/dist/deepl/docs/translation/javascript/DOC.md +887 -0
  34. package/dist/deepl/docs/translation/python/DOC.md +944 -0
  35. package/dist/deepseek/docs/llm/DOC.md +1220 -0
  36. package/dist/directus/docs/headless-cms/javascript/DOC.md +1128 -0
  37. package/dist/directus/docs/headless-cms/python/DOC.md +1276 -0
  38. package/dist/discord/docs/bot/javascript/DOC.md +1090 -0
  39. package/dist/discord/docs/bot/python/DOC.md +1130 -0
  40. package/dist/elasticsearch/docs/search/DOC.md +1634 -0
  41. package/dist/elevenlabs/docs/text-to-speech/javascript/DOC.md +336 -0
  42. package/dist/elevenlabs/docs/text-to-speech/python/DOC.md +552 -0
  43. package/dist/firebase/docs/auth/DOC.md +1015 -0
  44. package/dist/gemini/docs/genai/javascript/DOC.md +691 -0
  45. package/dist/gemini/docs/genai/python/DOC.md +555 -0
  46. package/dist/github/docs/octokit/DOC.md +1560 -0
  47. package/dist/google/docs/bigquery/javascript/DOC.md +1688 -0
  48. package/dist/google/docs/bigquery/python/DOC.md +1503 -0
  49. package/dist/hubspot/docs/crm/javascript/DOC.md +1805 -0
  50. package/dist/hubspot/docs/crm/python/DOC.md +2033 -0
  51. package/dist/huggingface/docs/transformers/DOC.md +948 -0
  52. package/dist/intercom/docs/messaging/javascript/DOC.md +1844 -0
  53. package/dist/intercom/docs/messaging/python/DOC.md +1797 -0
  54. package/dist/jira/docs/issues/javascript/DOC.md +1420 -0
  55. package/dist/jira/docs/issues/python/DOC.md +1492 -0
  56. package/dist/kafka/docs/streaming/javascript/DOC.md +1671 -0
  57. package/dist/kafka/docs/streaming/python/DOC.md +1464 -0
  58. package/dist/landingai-ade/docs/api/DOC.md +620 -0
  59. package/dist/landingai-ade/docs/sdk/python/DOC.md +489 -0
  60. package/dist/landingai-ade/docs/sdk/typescript/DOC.md +542 -0
  61. package/dist/landingai-ade/skills/SKILL.md +489 -0
  62. package/dist/launchdarkly/docs/feature-flags/javascript/DOC.md +1191 -0
  63. package/dist/launchdarkly/docs/feature-flags/python/DOC.md +1671 -0
  64. package/dist/linear/docs/tracker/DOC.md +1554 -0
  65. package/dist/livekit/docs/realtime/javascript/DOC.md +303 -0
  66. package/dist/livekit/docs/realtime/python/DOC.md +163 -0
  67. package/dist/mailchimp/docs/marketing/DOC.md +1420 -0
  68. package/dist/meilisearch/docs/search/DOC.md +1241 -0
  69. package/dist/microsoft/docs/onedrive/javascript/DOC.md +1421 -0
  70. package/dist/microsoft/docs/onedrive/python/DOC.md +1549 -0
  71. package/dist/mongodb/docs/atlas/DOC.md +2041 -0
  72. package/dist/notion/docs/workspace-api/javascript/DOC.md +1435 -0
  73. package/dist/notion/docs/workspace-api/python/DOC.md +1400 -0
  74. package/dist/okta/docs/identity/javascript/DOC.md +1171 -0
  75. package/dist/okta/docs/identity/python/DOC.md +1401 -0
  76. package/dist/openai/docs/chat/javascript/DOC.md +407 -0
  77. package/dist/openai/docs/chat/python/DOC.md +568 -0
  78. package/dist/paypal/docs/checkout/DOC.md +278 -0
  79. package/dist/pinecone/docs/sdk/javascript/DOC.md +984 -0
  80. package/dist/pinecone/docs/sdk/python/DOC.md +1395 -0
  81. package/dist/plaid/docs/banking/javascript/DOC.md +1163 -0
  82. package/dist/plaid/docs/banking/python/DOC.md +1203 -0
  83. package/dist/playwright-community/skills/login-flows/SKILL.md +108 -0
  84. package/dist/postmark/docs/transactional-email/DOC.md +1168 -0
  85. package/dist/prisma/docs/orm/javascript/DOC.md +1419 -0
  86. package/dist/prisma/docs/orm/python/DOC.md +1317 -0
  87. package/dist/qdrant/docs/vector-search/javascript/DOC.md +1221 -0
  88. package/dist/qdrant/docs/vector-search/python/DOC.md +1653 -0
  89. package/dist/rabbitmq/docs/message-queue/javascript/DOC.md +1193 -0
  90. package/dist/rabbitmq/docs/message-queue/python/DOC.md +1243 -0
  91. package/dist/razorpay/docs/payments/javascript/DOC.md +1219 -0
  92. package/dist/razorpay/docs/payments/python/DOC.md +1330 -0
  93. package/dist/redis/docs/key-value/javascript/DOC.md +1851 -0
  94. package/dist/redis/docs/key-value/python/DOC.md +2054 -0
  95. package/dist/registry.json +2817 -0
  96. package/dist/replicate/docs/model-hosting/DOC.md +1318 -0
  97. package/dist/resend/docs/email/DOC.md +1271 -0
  98. package/dist/salesforce/docs/crm/javascript/DOC.md +1241 -0
  99. package/dist/salesforce/docs/crm/python/DOC.md +1183 -0
  100. package/dist/search-index.json +1 -0
  101. package/dist/sendgrid/docs/email-api/javascript/DOC.md +371 -0
  102. package/dist/sendgrid/docs/email-api/python/DOC.md +656 -0
  103. package/dist/sentry/docs/error-tracking/javascript/DOC.md +1073 -0
  104. package/dist/sentry/docs/error-tracking/python/DOC.md +1309 -0
  105. package/dist/shopify/docs/storefront/DOC.md +457 -0
  106. package/dist/slack/docs/workspace/javascript/DOC.md +933 -0
  107. package/dist/slack/docs/workspace/python/DOC.md +271 -0
  108. package/dist/square/docs/payments/javascript/DOC.md +1855 -0
  109. package/dist/square/docs/payments/python/DOC.md +1728 -0
  110. package/dist/stripe/docs/api/DOC.md +1727 -0
  111. package/dist/stripe/docs/payments/DOC.md +1726 -0
  112. package/dist/stytch/docs/auth/javascript/DOC.md +1813 -0
  113. package/dist/stytch/docs/auth/python/DOC.md +1962 -0
  114. package/dist/supabase/docs/client/DOC.md +1606 -0
  115. package/dist/twilio/docs/messaging/python/DOC.md +469 -0
  116. package/dist/twilio/docs/messaging/typescript/DOC.md +946 -0
  117. package/dist/vercel/docs/platform/DOC.md +1940 -0
  118. package/dist/weaviate/docs/vector-db/javascript/DOC.md +1268 -0
  119. package/dist/weaviate/docs/vector-db/python/DOC.md +1388 -0
  120. package/dist/zendesk/docs/support/javascript/DOC.md +2150 -0
  121. package/dist/zendesk/docs/support/python/DOC.md +2297 -0
  122. package/package.json +22 -6
  123. package/skills/get-api-docs/SKILL.md +84 -0
  124. package/src/commands/annotate.js +83 -0
  125. package/src/commands/build.js +12 -1
  126. package/src/commands/feedback.js +150 -0
  127. package/src/commands/get.js +83 -42
  128. package/src/commands/search.js +7 -0
  129. package/src/index.js +43 -17
  130. package/src/lib/analytics.js +90 -0
  131. package/src/lib/annotations.js +57 -0
  132. package/src/lib/bm25.js +170 -0
  133. package/src/lib/cache.js +69 -6
  134. package/src/lib/config.js +8 -3
  135. package/src/lib/identity.js +99 -0
  136. package/src/lib/registry.js +103 -20
  137. package/src/lib/telemetry.js +86 -0
  138. package/src/mcp/server.js +177 -0
  139. package/src/mcp/tools.js +251 -0
@@ -0,0 +1,1851 @@
1
+ ---
2
+ name: key-value
3
+ description: "Redis JavaScript client (node-redis) for key-value storage, caching, and pub/sub messaging"
4
+ metadata:
5
+ languages: "javascript"
6
+ versions: "5.9.0"
7
+ updated-on: "2026-03-01"
8
+ source: maintainer
9
+ tags: "redis,database,cache,key-value,pubsub"
10
+ ---
11
+
12
+ # Redis JavaScript Client (node-redis) - Complete Integration Guide
13
+
14
+ ## GOLDEN RULE
15
+
16
+ **ALWAYS use the official `redis` npm package (node-redis) for Redis integration.**
17
+
18
+ ```bash
19
+ npm install redis
20
+ ```
21
+
22
+ **DO NOT use:**
23
+ - `ioredis` (alternative client, not official)
24
+ - `redis-node` (unofficial package)
25
+ - `node_redis` (deprecated naming)
26
+ - Any other third-party Redis clients unless specifically required
27
+
28
+ The official `redis` package is maintained by Redis and is the recommended client for Node.js applications.
29
+
30
+ ---
31
+
32
+ ## Installation
33
+
34
+ ### Basic Installation
35
+
36
+ ```bash
37
+ npm install redis
38
+ ```
39
+
40
+ ### Installation with TypeScript Support
41
+
42
+ ```bash
43
+ npm install redis
44
+ npm install --save-dev @types/node
45
+ ```
46
+
47
+ The `redis` package includes built-in TypeScript definitions.
48
+
49
+ ### Environment Setup
50
+
51
+ Create a `.env` file in your project root:
52
+
53
+ ```env
54
+ REDIS_HOST=localhost
55
+ REDIS_PORT=6379
56
+ REDIS_PASSWORD=your_password_here
57
+ REDIS_DB=0
58
+ REDIS_URL=redis://username:password@localhost:6379
59
+ ```
60
+
61
+ Install dotenv to load environment variables:
62
+
63
+ ```bash
64
+ npm install dotenv
65
+ ```
66
+
67
+ ---
68
+
69
+ ## Initialization
70
+
71
+ ### Basic Connection (Localhost)
72
+
73
+ ```javascript
74
+ import { createClient } from 'redis';
75
+
76
+ const client = createClient();
77
+
78
+ client.on('error', (err) => {
79
+ console.error('Redis Client Error', err);
80
+ });
81
+
82
+ await client.connect();
83
+
84
+ // Use the client...
85
+
86
+ await client.quit();
87
+ ```
88
+
89
+ ### Connection with Environment Variables
90
+
91
+ ```javascript
92
+ import { createClient } from 'redis';
93
+ import dotenv from 'dotenv';
94
+
95
+ dotenv.config();
96
+
97
+ const client = createClient({
98
+ socket: {
99
+ host: process.env.REDIS_HOST || 'localhost',
100
+ port: parseInt(process.env.REDIS_PORT || '6379')
101
+ },
102
+ password: process.env.REDIS_PASSWORD,
103
+ database: parseInt(process.env.REDIS_DB || '0')
104
+ });
105
+
106
+ client.on('error', (err) => {
107
+ console.error('Redis Client Error', err);
108
+ });
109
+
110
+ await client.connect();
111
+ ```
112
+
113
+ ### Connection Using URL
114
+
115
+ ```javascript
116
+ import { createClient } from 'redis';
117
+ import dotenv from 'dotenv';
118
+
119
+ dotenv.config();
120
+
121
+ const client = createClient({
122
+ url: process.env.REDIS_URL || 'redis://localhost:6379'
123
+ });
124
+
125
+ client.on('error', (err) => {
126
+ console.error('Redis Client Error', err);
127
+ });
128
+
129
+ await client.connect();
130
+ ```
131
+
132
+ ### Connection with Authentication
133
+
134
+ ```javascript
135
+ import { createClient } from 'redis';
136
+
137
+ const client = createClient({
138
+ url: 'redis://alice:foobared@awesome.redis.server:6380'
139
+ });
140
+
141
+ // Or using socket configuration
142
+ const clientAlt = createClient({
143
+ socket: {
144
+ host: 'awesome.redis.server',
145
+ port: 6380
146
+ },
147
+ username: 'alice',
148
+ password: 'foobared'
149
+ });
150
+
151
+ await client.connect();
152
+ ```
153
+
154
+ ### Connection with TLS/SSL
155
+
156
+ ```javascript
157
+ import { createClient } from 'redis';
158
+ import fs from 'fs';
159
+
160
+ const client = createClient({
161
+ socket: {
162
+ host: 'redis.example.com',
163
+ port: 6380,
164
+ tls: true,
165
+ key: fs.readFileSync('/path/to/client-key.pem'),
166
+ cert: fs.readFileSync('/path/to/client-cert.pem'),
167
+ ca: [fs.readFileSync('/path/to/ca-cert.pem')]
168
+ }
169
+ });
170
+
171
+ await client.connect();
172
+ ```
173
+
174
+ ### Connection Status Checks
175
+
176
+ ```javascript
177
+ // Check if client is ready to execute commands
178
+ if (client.isReady) {
179
+ console.log('Client is ready');
180
+ }
181
+
182
+ // Check if underlying socket is open
183
+ if (client.isOpen) {
184
+ console.log('Connection is open');
185
+ }
186
+ ```
187
+
188
+ ### Duplicate Connection (for Pub/Sub)
189
+
190
+ ```javascript
191
+ const client = createClient();
192
+ await client.connect();
193
+
194
+ // Create a duplicate connection for pub/sub
195
+ const subscriber = client.duplicate();
196
+ await subscriber.connect();
197
+ ```
198
+
199
+ ### Graceful Shutdown
200
+
201
+ ```javascript
202
+ // Graceful disconnect
203
+ await client.quit();
204
+
205
+ // Force disconnect
206
+ await client.disconnect();
207
+ ```
208
+
209
+ ---
210
+
211
+ ## Core API Surfaces
212
+
213
+ ## String Operations
214
+
215
+ ### Basic SET and GET
216
+
217
+ ```javascript
218
+ // Set a string value
219
+ await client.set('key', 'value');
220
+
221
+ // Get a string value
222
+ const value = await client.get('key');
223
+ console.log(value); // 'value'
224
+ ```
225
+
226
+ ### SET with Options
227
+
228
+ ```javascript
229
+ // Set with expiration (EX = seconds)
230
+ await client.set('session:123', 'user_data', {
231
+ EX: 3600 // expires in 1 hour
232
+ });
233
+
234
+ // Set with expiration (PX = milliseconds)
235
+ await client.set('temp:key', 'value', {
236
+ PX: 5000 // expires in 5 seconds
237
+ });
238
+
239
+ // Set only if key doesn't exist (NX)
240
+ await client.set('key', 'value', {
241
+ NX: true
242
+ });
243
+
244
+ // Set only if key exists (XX)
245
+ await client.set('key', 'new_value', {
246
+ XX: true
247
+ });
248
+
249
+ // Get old value and set new value
250
+ const oldValue = await client.set('key', 'new_value', {
251
+ GET: true
252
+ });
253
+ ```
254
+
255
+ ### Multiple Keys
256
+
257
+ ```javascript
258
+ // Set multiple keys at once
259
+ await client.mSet({
260
+ 'key1': 'value1',
261
+ 'key2': 'value2',
262
+ 'key3': 'value3'
263
+ });
264
+
265
+ // Get multiple keys at once
266
+ const values = await client.mGet(['key1', 'key2', 'key3']);
267
+ console.log(values); // ['value1', 'value2', 'value3']
268
+ ```
269
+
270
+ ### Increment and Decrement
271
+
272
+ ```javascript
273
+ // Set initial value
274
+ await client.set('counter', 0);
275
+
276
+ // Increment by 1
277
+ await client.incr('counter'); // Returns 1
278
+
279
+ // Increment by specific amount
280
+ await client.incrBy('counter', 10); // Returns 11
281
+
282
+ // Increment float
283
+ await client.incrByFloat('price', 2.5); // Increment by 2.5
284
+
285
+ // Decrement by 1
286
+ await client.decr('counter');
287
+
288
+ // Decrement by specific amount
289
+ await client.decrBy('counter', 5);
290
+ ```
291
+
292
+ ### String Manipulation
293
+
294
+ ```javascript
295
+ // Append to string
296
+ await client.set('message', 'Hello');
297
+ await client.append('message', ' World'); // 'Hello World'
298
+
299
+ // Get substring
300
+ const substr = await client.getRange('message', 0, 4); // 'Hello'
301
+
302
+ // Get length
303
+ const len = await client.strLen('message'); // 11
304
+
305
+ // Set range
306
+ await client.setRange('message', 6, 'Redis'); // 'Hello Redis'
307
+ ```
308
+
309
+ ---
310
+
311
+ ## Hash Operations
312
+
313
+ ### Basic Hash Operations
314
+
315
+ ```javascript
316
+ // Set a single field in a hash
317
+ await client.hSet('user:1000', 'name', 'John Doe');
318
+
319
+ // Get a single field from a hash
320
+ const name = await client.hGet('user:1000', 'name');
321
+ console.log(name); // 'John Doe'
322
+
323
+ // Set multiple fields at once
324
+ await client.hSet('user:1000', {
325
+ name: 'John Doe',
326
+ email: '[email protected]',
327
+ age: 30
328
+ });
329
+
330
+ // Get all fields and values
331
+ const user = await client.hGetAll('user:1000');
332
+ console.log(user);
333
+ // { name: 'John Doe', email: '[email protected]', age: '30' }
334
+ ```
335
+
336
+ ### Advanced Hash Operations
337
+
338
+ ```javascript
339
+ // Check if field exists
340
+ const exists = await client.hExists('user:1000', 'email'); // true
341
+
342
+ // Get all field names
343
+ const fields = await client.hKeys('user:1000');
344
+ // ['name', 'email', 'age']
345
+
346
+ // Get all values
347
+ const values = await client.hVals('user:1000');
348
+ // ['John Doe', '[email protected]', '30']
349
+
350
+ // Get number of fields
351
+ const count = await client.hLen('user:1000'); // 3
352
+
353
+ // Get multiple fields
354
+ const userData = await client.hmGet('user:1000', ['name', 'email']);
355
+ // ['John Doe', '[email protected]']
356
+
357
+ // Delete fields
358
+ await client.hDel('user:1000', 'age');
359
+
360
+ // Increment numeric field
361
+ await client.hSet('user:1000', 'loginCount', 0);
362
+ await client.hIncrBy('user:1000', 'loginCount', 1); // 1
363
+
364
+ // Increment float field
365
+ await client.hIncrByFloat('user:1000', 'balance', 10.50);
366
+
367
+ // Set only if field doesn't exist
368
+ await client.hSetNX('user:1000', 'created', Date.now());
369
+ ```
370
+
371
+ ### Scan Hash Fields
372
+
373
+ ```javascript
374
+ // Scan hash fields (for large hashes)
375
+ for await (const { field, value } of client.hScanIterator('user:1000')) {
376
+ console.log(`${field}: ${value}`);
377
+ }
378
+ ```
379
+
380
+ ---
381
+
382
+ ## List Operations
383
+
384
+ ### Basic List Operations
385
+
386
+ ```javascript
387
+ // Push elements to the right (end) of list
388
+ await client.rPush('tasks', 'task1');
389
+ await client.rPush('tasks', ['task2', 'task3']); // Multiple values
390
+
391
+ // Push elements to the left (beginning) of list
392
+ await client.lPush('tasks', 'urgent_task');
393
+ await client.lPush('tasks', ['task0', 'task-1']);
394
+
395
+ // Get list length
396
+ const length = await client.lLen('tasks');
397
+
398
+ // Get range of elements
399
+ const tasks = await client.lRange('tasks', 0, -1); // Get all
400
+ const firstThree = await client.lRange('tasks', 0, 2); // Get first 3
401
+
402
+ // Get element by index
403
+ const task = await client.lIndex('tasks', 0);
404
+
405
+ // Pop from right (end)
406
+ const lastTask = await client.rPop('tasks');
407
+
408
+ // Pop from left (beginning)
409
+ const firstTask = await client.lPop('tasks');
410
+ ```
411
+
412
+ ### Advanced List Operations
413
+
414
+ ```javascript
415
+ // Blocking pop (wait for element)
416
+ const task = await client.blPop('tasks', 10); // Wait up to 10 seconds
417
+ // Returns: { key: 'tasks', element: 'task1' }
418
+
419
+ const taskRight = await client.brPop('tasks', 10);
420
+
421
+ // Set element at index
422
+ await client.lSet('tasks', 0, 'updated_task');
423
+
424
+ // Insert before/after element
425
+ await client.lInsert('tasks', 'BEFORE', 'task2', 'new_task');
426
+ await client.lInsert('tasks', 'AFTER', 'task2', 'another_task');
427
+
428
+ // Remove elements
429
+ await client.lRem('tasks', 2, 'task1'); // Remove first 2 occurrences
430
+ await client.lRem('tasks', -1, 'task2'); // Remove last occurrence
431
+ await client.lRem('tasks', 0, 'task3'); // Remove all occurrences
432
+
433
+ // Trim list to range
434
+ await client.lTrim('tasks', 0, 9); // Keep only first 10 elements
435
+
436
+ // Move element between lists
437
+ const element = await client.rPopLPush('source', 'destination');
438
+
439
+ // Blocking move
440
+ const moved = await client.brPopLPush('source', 'destination', 5);
441
+ ```
442
+
443
+ ---
444
+
445
+ ## Set Operations
446
+
447
+ ### Basic Set Operations
448
+
449
+ ```javascript
450
+ // Add members to set
451
+ await client.sAdd('tags', 'javascript');
452
+ await client.sAdd('tags', ['nodejs', 'redis', 'database']);
453
+
454
+ // Check if member exists
455
+ const exists = await client.sIsMember('tags', 'nodejs'); // true
456
+
457
+ // Get all members
458
+ const allTags = await client.sMembers('tags');
459
+ // ['javascript', 'nodejs', 'redis', 'database']
460
+
461
+ // Get number of members
462
+ const count = await client.sCard('tags'); // 4
463
+
464
+ // Remove members
465
+ await client.sRem('tags', 'database');
466
+ await client.sRem('tags', ['nodejs', 'redis']);
467
+
468
+ // Pop random member
469
+ const randomTag = await client.sPop('tags');
470
+
471
+ // Get random member without removing
472
+ const random = await client.sRandMember('tags');
473
+ const randomThree = await client.sRandMemberCount('tags', 3);
474
+ ```
475
+
476
+ ### Set Operations Between Multiple Sets
477
+
478
+ ```javascript
479
+ // Create sets
480
+ await client.sAdd('set1', ['a', 'b', 'c']);
481
+ await client.sAdd('set2', ['b', 'c', 'd']);
482
+ await client.sAdd('set3', ['c', 'd', 'e']);
483
+
484
+ // Union (combine all unique members)
485
+ const union = await client.sUnion(['set1', 'set2']);
486
+ // ['a', 'b', 'c', 'd']
487
+
488
+ // Store union in new set
489
+ await client.sUnionStore('result', ['set1', 'set2']);
490
+
491
+ // Intersection (common members)
492
+ const inter = await client.sInter(['set1', 'set2']);
493
+ // ['b', 'c']
494
+
495
+ // Store intersection
496
+ await client.sInterStore('result', ['set1', 'set2']);
497
+
498
+ // Difference (members in first set but not in others)
499
+ const diff = await client.sDiff(['set1', 'set2']);
500
+ // ['a']
501
+
502
+ // Store difference
503
+ await client.sDiffStore('result', ['set1', 'set2']);
504
+
505
+ // Move member between sets
506
+ await client.sMove('set1', 'set2', 'a');
507
+ ```
508
+
509
+ ### Scan Set Members
510
+
511
+ ```javascript
512
+ // Scan set members (for large sets)
513
+ for await (const member of client.sScanIterator('tags')) {
514
+ console.log(member);
515
+ }
516
+ ```
517
+
518
+ ---
519
+
520
+ ## Sorted Set Operations
521
+
522
+ ### Basic Sorted Set Operations
523
+
524
+ ```javascript
525
+ // Add members with scores
526
+ await client.zAdd('leaderboard', { score: 100, value: 'player1' });
527
+ await client.zAdd('leaderboard', [
528
+ { score: 200, value: 'player2' },
529
+ { score: 150, value: 'player3' }
530
+ ]);
531
+
532
+ // Get rank (0-based, lowest to highest)
533
+ const rank = await client.zRank('leaderboard', 'player1'); // 0
534
+
535
+ // Get reverse rank (highest to lowest)
536
+ const revRank = await client.zRevRank('leaderboard', 'player2'); // 0
537
+
538
+ // Get score
539
+ const score = await client.zScore('leaderboard', 'player2'); // 200
540
+
541
+ // Get number of members
542
+ const count = await client.zCard('leaderboard'); // 3
543
+
544
+ // Increment score
545
+ await client.zIncrBy('leaderboard', 50, 'player1'); // 150
546
+ ```
547
+
548
+ ### Range Queries
549
+
550
+ ```javascript
551
+ // Get range by rank (lowest to highest)
552
+ const bottom3 = await client.zRange('leaderboard', 0, 2);
553
+ // ['player1', 'player3', 'player2']
554
+
555
+ // Get range with scores
556
+ const withScores = await client.zRangeWithScores('leaderboard', 0, 2);
557
+ // [{ value: 'player1', score: 150 }, ...]
558
+
559
+ // Get range by rank (highest to lowest)
560
+ const top3 = await client.zRevRange('leaderboard', 0, 2);
561
+
562
+ // Get range by score
563
+ const range = await client.zRangeByScore('leaderboard', 100, 200);
564
+
565
+ // Get range by score with limit
566
+ const limited = await client.zRangeByScore('leaderboard', 100, 200, {
567
+ LIMIT: {
568
+ offset: 0,
569
+ count: 10
570
+ }
571
+ });
572
+
573
+ // Get reverse range by score
574
+ const revRange = await client.zRevRangeByScore('leaderboard', 200, 100);
575
+ ```
576
+
577
+ ### Advanced Sorted Set Operations
578
+
579
+ ```javascript
580
+ // Count members in score range
581
+ const count = await client.zCount('leaderboard', 100, 200);
582
+
583
+ // Count members by lexicographical range
584
+ const lexCount = await client.zLexCount('leaderboard', '[a', '[z');
585
+
586
+ // Remove members
587
+ await client.zRem('leaderboard', 'player1');
588
+ await client.zRem('leaderboard', ['player2', 'player3']);
589
+
590
+ // Remove by rank range
591
+ await client.zRemRangeByRank('leaderboard', 0, 1); // Remove bottom 2
592
+
593
+ // Remove by score range
594
+ await client.zRemRangeByScore('leaderboard', 0, 100);
595
+
596
+ // Pop highest/lowest scoring member
597
+ const highest = await client.zPopMax('leaderboard');
598
+ const lowest = await client.zPopMin('leaderboard');
599
+
600
+ // Pop with count
601
+ const top3 = await client.zPopMax('leaderboard', 3);
602
+
603
+ // Blocking pop
604
+ const member = await client.bzPopMax('leaderboard', 5); // Wait up to 5 sec
605
+ const minMember = await client.bzPopMin('leaderboard', 5);
606
+ ```
607
+
608
+ ### Sorted Set Operations Between Multiple Sets
609
+
610
+ ```javascript
611
+ // Union of sorted sets
612
+ await client.zUnionStore('result', ['set1', 'set2']);
613
+
614
+ // Union with weights
615
+ await client.zUnionStore('result', ['set1', 'set2'], {
616
+ WEIGHTS: [2, 3]
617
+ });
618
+
619
+ // Union with aggregate function
620
+ await client.zUnionStore('result', ['set1', 'set2'], {
621
+ AGGREGATE: 'MAX' // or 'MIN', 'SUM'
622
+ });
623
+
624
+ // Intersection
625
+ await client.zInterStore('result', ['set1', 'set2']);
626
+
627
+ // Intersection with weights and aggregate
628
+ await client.zInterStore('result', ['set1', 'set2'], {
629
+ WEIGHTS: [1, 2],
630
+ AGGREGATE: 'SUM'
631
+ });
632
+ ```
633
+
634
+ ### Scan Sorted Set
635
+
636
+ ```javascript
637
+ // Scan sorted set members
638
+ for await (const member of client.zScanIterator('leaderboard')) {
639
+ console.log(member);
640
+ }
641
+ ```
642
+
643
+ ---
644
+
645
+ ## Key Management Operations
646
+
647
+ ### Key Operations
648
+
649
+ ```javascript
650
+ // Check if key exists
651
+ const exists = await client.exists('mykey'); // 1 if exists, 0 if not
652
+ const multiExists = await client.exists(['key1', 'key2']); // Count
653
+
654
+ // Delete keys
655
+ await client.del('mykey');
656
+ await client.del(['key1', 'key2', 'key3']);
657
+
658
+ // Set expiration in seconds
659
+ await client.expire('mykey', 60); // Expire in 60 seconds
660
+
661
+ // Set expiration at specific timestamp
662
+ const timestamp = Math.floor(Date.now() / 1000) + 3600;
663
+ await client.expireAt('mykey', timestamp);
664
+
665
+ // Set expiration in milliseconds
666
+ await client.pExpire('mykey', 60000); // 60 seconds
667
+
668
+ // Get time to live in seconds
669
+ const ttl = await client.ttl('mykey'); // -1 if no expiration, -2 if not exists
670
+
671
+ // Get time to live in milliseconds
672
+ const pttl = await client.pTtl('mykey');
673
+
674
+ // Remove expiration
675
+ await client.persist('mykey');
676
+
677
+ // Rename key
678
+ await client.rename('oldkey', 'newkey');
679
+
680
+ // Rename only if new key doesn't exist
681
+ const renamed = await client.renameNX('oldkey', 'newkey');
682
+
683
+ // Get key type
684
+ const type = await client.type('mykey'); // 'string', 'list', 'set', etc.
685
+
686
+ // Get random key
687
+ const randomKey = await client.randomKey();
688
+ ```
689
+
690
+ ### Scanning Keys
691
+
692
+ ```javascript
693
+ // Scan all keys (use instead of KEYS for production)
694
+ const keys = [];
695
+ for await (const key of client.scanIterator()) {
696
+ keys.push(key);
697
+ }
698
+
699
+ // Scan with pattern
700
+ for await (const key of client.scanIterator({
701
+ MATCH: 'user:*',
702
+ COUNT: 100
703
+ })) {
704
+ console.log(key);
705
+ }
706
+
707
+ // Scan specific key type
708
+ for await (const key of client.scanIterator({
709
+ TYPE: 'string',
710
+ COUNT: 100
711
+ })) {
712
+ console.log(key);
713
+ }
714
+ ```
715
+
716
+ ---
717
+
718
+ ## Transactions
719
+
720
+ ### Basic Transaction (MULTI/EXEC)
721
+
722
+ ```javascript
723
+ // Execute multiple commands atomically
724
+ await client.set('another-key', 'another-value');
725
+
726
+ const results = await client
727
+ .multi()
728
+ .set('key', 'value')
729
+ .get('another-key')
730
+ .incr('counter')
731
+ .exec();
732
+
733
+ console.log(results); // ['OK', 'another-value', 1]
734
+ ```
735
+
736
+ ### Transaction with Error Handling
737
+
738
+ ```javascript
739
+ try {
740
+ const results = await client
741
+ .multi()
742
+ .set('key1', 'value1')
743
+ .set('key2', 'value2')
744
+ .get('key1')
745
+ .exec();
746
+
747
+ // Check each result
748
+ results.forEach((result, index) => {
749
+ if (result instanceof Error) {
750
+ console.error(`Command ${index} failed:`, result);
751
+ } else {
752
+ console.log(`Command ${index} result:`, result);
753
+ }
754
+ });
755
+ } catch (error) {
756
+ console.error('Transaction failed:', error);
757
+ }
758
+ ```
759
+
760
+ ### Transaction with WATCH (Optimistic Locking)
761
+
762
+ ```javascript
763
+ // Watch a key for changes
764
+ await client.watch('balance');
765
+
766
+ const balance = parseInt(await client.get('balance'));
767
+
768
+ if (balance >= 100) {
769
+ // This transaction will fail if 'balance' changed after WATCH
770
+ const results = await client
771
+ .multi()
772
+ .decrBy('balance', 100)
773
+ .incrBy('purchases', 1)
774
+ .exec();
775
+
776
+ if (results === null) {
777
+ console.log('Transaction aborted - balance was modified');
778
+ } else {
779
+ console.log('Purchase successful');
780
+ }
781
+ } else {
782
+ await client.unwatch();
783
+ console.log('Insufficient balance');
784
+ }
785
+ ```
786
+
787
+ ### Complex Transaction Example
788
+
789
+ ```javascript
790
+ async function transferMoney(fromAccount, toAccount, amount) {
791
+ await client.watch([fromAccount, toAccount]);
792
+
793
+ const fromBalance = parseFloat(await client.get(fromAccount));
794
+
795
+ if (fromBalance < amount) {
796
+ await client.unwatch();
797
+ throw new Error('Insufficient funds');
798
+ }
799
+
800
+ const results = await client
801
+ .multi()
802
+ .decrByFloat(fromAccount, amount)
803
+ .incrByFloat(toAccount, amount)
804
+ .exec();
805
+
806
+ if (results === null) {
807
+ throw new Error('Transaction failed - accounts modified during transfer');
808
+ }
809
+
810
+ return results;
811
+ }
812
+
813
+ // Usage
814
+ try {
815
+ await transferMoney('account:1', 'account:2', 50.00);
816
+ console.log('Transfer successful');
817
+ } catch (error) {
818
+ console.error('Transfer failed:', error.message);
819
+ }
820
+ ```
821
+
822
+ ---
823
+
824
+ ## Pipelining
825
+
826
+ ### Automatic Pipelining
827
+
828
+ Node-redis automatically pipelines commands that are queued in the same tick:
829
+
830
+ ```javascript
831
+ // These commands are automatically batched and sent together
832
+ const [result1, result2, result3] = await Promise.all([
833
+ client.set('key1', 'value1'),
834
+ client.set('key2', 'value2'),
835
+ client.get('key1')
836
+ ]);
837
+
838
+ console.log(result1); // 'OK'
839
+ console.log(result2); // 'OK'
840
+ console.log(result3); // 'value1'
841
+ ```
842
+
843
+ ### Manual Pipelining with Multi (No Atomicity)
844
+
845
+ ```javascript
846
+ // Execute commands as pipeline without transaction semantics
847
+ const results = await client
848
+ .multi()
849
+ .set('key1', 'value1')
850
+ .set('key2', 'value2')
851
+ .get('key1')
852
+ .mGet(['key1', 'key2'])
853
+ .exec();
854
+
855
+ console.log(results);
856
+ // ['OK', 'OK', 'value1', ['value1', 'value2']]
857
+ ```
858
+
859
+ ### Large Batch Operations
860
+
861
+ ```javascript
862
+ async function batchSetKeys(keyValuePairs) {
863
+ const pipeline = client.multi();
864
+
865
+ for (const [key, value] of Object.entries(keyValuePairs)) {
866
+ pipeline.set(key, value);
867
+ }
868
+
869
+ const results = await pipeline.exec();
870
+ return results;
871
+ }
872
+
873
+ // Usage
874
+ const data = {
875
+ 'user:1': 'Alice',
876
+ 'user:2': 'Bob',
877
+ 'user:3': 'Charlie'
878
+ };
879
+
880
+ await batchSetKeys(data);
881
+ ```
882
+
883
+ ---
884
+
885
+ ## Pub/Sub
886
+
887
+ ### Basic Subscriber
888
+
889
+ ```javascript
890
+ import { createClient } from 'redis';
891
+
892
+ const client = createClient();
893
+ await client.connect();
894
+
895
+ // Create dedicated connection for subscribing
896
+ const subscriber = client.duplicate();
897
+ await subscriber.connect();
898
+
899
+ // Subscribe to channel
900
+ await subscriber.subscribe('notifications', (message) => {
901
+ console.log('Received message:', message);
902
+ });
903
+
904
+ console.log('Subscribed to notifications channel');
905
+ ```
906
+
907
+ ### Basic Publisher
908
+
909
+ ```javascript
910
+ import { createClient } from 'redis';
911
+
912
+ const publisher = createClient();
913
+ await publisher.connect();
914
+
915
+ // Publish messages
916
+ await publisher.publish('notifications', 'Hello, World!');
917
+ await publisher.publish('notifications', JSON.stringify({
918
+ type: 'alert',
919
+ message: 'System update'
920
+ }));
921
+
922
+ console.log('Messages published');
923
+ ```
924
+
925
+ ### Multiple Channels
926
+
927
+ ```javascript
928
+ // Subscribe to multiple channels
929
+ await subscriber.subscribe('channel1', (message) => {
930
+ console.log('Channel1:', message);
931
+ });
932
+
933
+ await subscriber.subscribe('channel2', (message) => {
934
+ console.log('Channel2:', message);
935
+ });
936
+
937
+ await subscriber.subscribe('channel3', (message) => {
938
+ console.log('Channel3:', message);
939
+ });
940
+ ```
941
+
942
+ ### Pattern-Based Subscription
943
+
944
+ ```javascript
945
+ // Subscribe to channels matching pattern
946
+ await subscriber.pSubscribe('user:*', (message, channel) => {
947
+ console.log(`Message from ${channel}:`, message);
948
+ });
949
+
950
+ // Publish to matching channels
951
+ await publisher.publish('user:1000', 'User 1000 logged in');
952
+ await publisher.publish('user:2000', 'User 2000 logged out');
953
+ ```
954
+
955
+ ### Unsubscribe
956
+
957
+ ```javascript
958
+ // Unsubscribe from specific channel
959
+ await subscriber.unsubscribe('channel1');
960
+
961
+ // Unsubscribe from all channels
962
+ await subscriber.unsubscribe();
963
+
964
+ // Unsubscribe from pattern
965
+ await subscriber.pUnsubscribe('user:*');
966
+ ```
967
+
968
+ ### Complete Pub/Sub Example
969
+
970
+ ```javascript
971
+ import { createClient } from 'redis';
972
+
973
+ // Subscriber setup
974
+ async function setupSubscriber() {
975
+ const client = createClient();
976
+ await client.connect();
977
+
978
+ const subscriber = client.duplicate();
979
+ await subscriber.connect();
980
+
981
+ await subscriber.subscribe('chat:room1', (message) => {
982
+ const data = JSON.parse(message);
983
+ console.log(`${data.user}: ${data.text}`);
984
+ });
985
+
986
+ return subscriber;
987
+ }
988
+
989
+ // Publisher setup
990
+ async function setupPublisher() {
991
+ const publisher = createClient();
992
+ await publisher.connect();
993
+ return publisher;
994
+ }
995
+
996
+ // Usage
997
+ const subscriber = await setupSubscriber();
998
+ const publisher = await setupPublisher();
999
+
1000
+ // Publish chat messages
1001
+ await publisher.publish('chat:room1', JSON.stringify({
1002
+ user: 'Alice',
1003
+ text: 'Hello everyone!'
1004
+ }));
1005
+
1006
+ await publisher.publish('chat:room1', JSON.stringify({
1007
+ user: 'Bob',
1008
+ text: 'Hi Alice!'
1009
+ }));
1010
+ ```
1011
+
1012
+ ---
1013
+
1014
+ ## Redis Streams
1015
+
1016
+ ### Add to Stream (XADD)
1017
+
1018
+ ```javascript
1019
+ // Add entry to stream
1020
+ const id = await client.xAdd('events', '*', {
1021
+ user: 'alice',
1022
+ action: 'login',
1023
+ timestamp: Date.now()
1024
+ });
1025
+
1026
+ console.log('Entry ID:', id); // '1234567890123-0'
1027
+
1028
+ // Add with specific ID
1029
+ await client.xAdd('events', '1234567890123-1', {
1030
+ user: 'bob',
1031
+ action: 'logout'
1032
+ });
1033
+
1034
+ // Add with maxLen (limit stream size)
1035
+ await client.xAdd('events', '*', {
1036
+ user: 'charlie',
1037
+ action: 'signup'
1038
+ }, {
1039
+ TRIM: {
1040
+ strategy: 'MAXLEN',
1041
+ strategyModifier: '~', // Approximate
1042
+ threshold: 1000
1043
+ }
1044
+ });
1045
+ ```
1046
+
1047
+ ### Read from Stream (XREAD)
1048
+
1049
+ ```javascript
1050
+ // Read new entries
1051
+ const messages = await client.xRead(
1052
+ { key: 'events', id: '0' },
1053
+ { COUNT: 10 }
1054
+ );
1055
+
1056
+ console.log(messages);
1057
+ // [{ name: 'events', messages: [{ id: '...', message: {...} }] }]
1058
+
1059
+ // Read from multiple streams
1060
+ const multiStream = await client.xRead([
1061
+ { key: 'stream1', id: '0' },
1062
+ { key: 'stream2', id: '0' }
1063
+ ]);
1064
+
1065
+ // Blocking read (wait for new entries)
1066
+ const newMessages = await client.xRead(
1067
+ { key: 'events', id: '$' }, // '$' means only new messages
1068
+ { BLOCK: 5000 } // Wait up to 5 seconds
1069
+ );
1070
+ ```
1071
+
1072
+ ### Stream Range Queries
1073
+
1074
+ ```javascript
1075
+ // Read all entries
1076
+ const all = await client.xRange('events', '-', '+');
1077
+
1078
+ // Read range with IDs
1079
+ const range = await client.xRange('events', '1234567890000', '1234567899999');
1080
+
1081
+ // Read with limit
1082
+ const limited = await client.xRange('events', '-', '+', { COUNT: 100 });
1083
+
1084
+ // Reverse range
1085
+ const reverse = await client.xRevRange('events', '+', '-', { COUNT: 10 });
1086
+ ```
1087
+
1088
+ ### Stream Length and Info
1089
+
1090
+ ```javascript
1091
+ // Get stream length
1092
+ const length = await client.xLen('events');
1093
+
1094
+ // Get stream info
1095
+ const info = await client.xInfo('events');
1096
+ ```
1097
+
1098
+ ### Consumer Groups
1099
+
1100
+ ```javascript
1101
+ // Create consumer group
1102
+ await client.xGroupCreate('events', 'processors', '0', {
1103
+ MKSTREAM: true // Create stream if it doesn't exist
1104
+ });
1105
+
1106
+ // Read as consumer group
1107
+ const messages = await client.xReadGroup(
1108
+ 'processors',
1109
+ 'consumer1',
1110
+ { key: 'events', id: '>' }, // '>' means undelivered messages
1111
+ { COUNT: 10, BLOCK: 5000 }
1112
+ );
1113
+
1114
+ // Process messages and acknowledge
1115
+ for (const stream of messages) {
1116
+ for (const message of stream.messages) {
1117
+ // Process message
1118
+ console.log('Processing:', message.id, message.message);
1119
+
1120
+ // Acknowledge message
1121
+ await client.xAck('events', 'processors', message.id);
1122
+ }
1123
+ }
1124
+ ```
1125
+
1126
+ ### Delete from Stream
1127
+
1128
+ ```javascript
1129
+ // Delete specific entries
1130
+ await client.xDel('events', ['1234567890123-0', '1234567890123-1']);
1131
+
1132
+ // Trim stream
1133
+ await client.xTrim('events', 'MAXLEN', 1000);
1134
+
1135
+ // Approximate trim
1136
+ await client.xTrim('events', 'MAXLEN', { strategyModifier: '~', threshold: 1000 });
1137
+ ```
1138
+
1139
+ ### Complete Stream Example
1140
+
1141
+ ```javascript
1142
+ import { createClient } from 'redis';
1143
+
1144
+ const client = createClient();
1145
+ await client.connect();
1146
+
1147
+ // Producer
1148
+ async function addEvent(eventData) {
1149
+ return await client.xAdd('events', '*', eventData);
1150
+ }
1151
+
1152
+ // Consumer with consumer group
1153
+ async function processEvents() {
1154
+ // Create group if not exists
1155
+ try {
1156
+ await client.xGroupCreate('events', 'workers', '0', { MKSTREAM: true });
1157
+ } catch (err) {
1158
+ // Group already exists
1159
+ }
1160
+
1161
+ while (true) {
1162
+ const messages = await client.xReadGroup(
1163
+ 'workers',
1164
+ 'worker1',
1165
+ { key: 'events', id: '>' },
1166
+ { COUNT: 10, BLOCK: 5000 }
1167
+ );
1168
+
1169
+ if (messages && messages.length > 0) {
1170
+ for (const stream of messages) {
1171
+ for (const message of stream.messages) {
1172
+ try {
1173
+ // Process event
1174
+ console.log('Processing event:', message.message);
1175
+
1176
+ // Acknowledge
1177
+ await client.xAck('events', 'workers', message.id);
1178
+ } catch (error) {
1179
+ console.error('Error processing:', error);
1180
+ }
1181
+ }
1182
+ }
1183
+ }
1184
+ }
1185
+ }
1186
+
1187
+ // Usage
1188
+ await addEvent({ type: 'user_login', user: 'alice' });
1189
+ await addEvent({ type: 'user_logout', user: 'bob' });
1190
+
1191
+ // Start consumer
1192
+ processEvents();
1193
+ ```
1194
+
1195
+ ---
1196
+
1197
+ ## Scripting with Lua
1198
+
1199
+ ### Basic Script Execution (EVAL)
1200
+
1201
+ ```javascript
1202
+ // Execute Lua script
1203
+ const result = await client.eval(
1204
+ `return redis.call('SET', KEYS[1], ARGV[1])`,
1205
+ {
1206
+ keys: ['mykey'],
1207
+ arguments: ['myvalue']
1208
+ }
1209
+ );
1210
+
1211
+ console.log(result); // 'OK'
1212
+ ```
1213
+
1214
+ ### Script with Multiple Operations
1215
+
1216
+ ```javascript
1217
+ const script = `
1218
+ local current = redis.call('GET', KEYS[1])
1219
+ if current == false then
1220
+ current = 0
1221
+ end
1222
+ local new = tonumber(current) + tonumber(ARGV[1])
1223
+ redis.call('SET', KEYS[1], new)
1224
+ return new
1225
+ `;
1226
+
1227
+ const newValue = await client.eval(script, {
1228
+ keys: ['counter'],
1229
+ arguments: ['5']
1230
+ });
1231
+
1232
+ console.log('New counter value:', newValue);
1233
+ ```
1234
+
1235
+ ### Load and Execute Scripts (SCRIPT LOAD / EVALSHA)
1236
+
1237
+ ```javascript
1238
+ // Load script and get SHA1
1239
+ const sha = await client.scriptLoad(`
1240
+ return redis.call('GET', KEYS[1])
1241
+ `);
1242
+
1243
+ console.log('Script SHA:', sha);
1244
+
1245
+ // Execute by SHA1 (more efficient for repeated calls)
1246
+ const value = await client.evalSha(sha, {
1247
+ keys: ['mykey'],
1248
+ arguments: []
1249
+ });
1250
+ ```
1251
+
1252
+ ### Rate Limiting with Lua Script
1253
+
1254
+ ```javascript
1255
+ const rateLimitScript = `
1256
+ local key = KEYS[1]
1257
+ local limit = tonumber(ARGV[1])
1258
+ local window = tonumber(ARGV[2])
1259
+ local current = redis.call('INCR', key)
1260
+ if current == 1 then
1261
+ redis.call('EXPIRE', key, window)
1262
+ end
1263
+ if current > limit then
1264
+ return 0
1265
+ else
1266
+ return 1
1267
+ end
1268
+ `;
1269
+
1270
+ async function checkRateLimit(userId, limit = 10, window = 60) {
1271
+ const allowed = await client.eval(rateLimitScript, {
1272
+ keys: [`ratelimit:${userId}`],
1273
+ arguments: [limit.toString(), window.toString()]
1274
+ });
1275
+
1276
+ return allowed === 1;
1277
+ }
1278
+
1279
+ // Usage
1280
+ const allowed = await checkRateLimit('user:1000', 10, 60);
1281
+ if (allowed) {
1282
+ console.log('Request allowed');
1283
+ } else {
1284
+ console.log('Rate limit exceeded');
1285
+ }
1286
+ ```
1287
+
1288
+ ### Atomic Get and Delete
1289
+
1290
+ ```javascript
1291
+ const getAndDelete = `
1292
+ local value = redis.call('GET', KEYS[1])
1293
+ if value then
1294
+ redis.call('DEL', KEYS[1])
1295
+ end
1296
+ return value
1297
+ `;
1298
+
1299
+ const value = await client.eval(getAndDelete, {
1300
+ keys: ['temp:key'],
1301
+ arguments: []
1302
+ });
1303
+ ```
1304
+
1305
+ ---
1306
+
1307
+ ## JSON Support (RedisJSON)
1308
+
1309
+ ### Basic JSON Operations
1310
+
1311
+ ```javascript
1312
+ // Set JSON document
1313
+ await client.json.set('user:1', '$', {
1314
+ name: 'John Doe',
1315
+ email: '[email protected]',
1316
+ age: 30,
1317
+ tags: ['developer', 'redis']
1318
+ });
1319
+
1320
+ // Get entire JSON document
1321
+ const user = await client.json.get('user:1');
1322
+ console.log(user);
1323
+
1324
+ // Get specific path
1325
+ const name = await client.json.get('user:1', { path: '$.name' });
1326
+ const age = await client.json.get('user:1', { path: '$.age' });
1327
+ ```
1328
+
1329
+ ### Update JSON Fields
1330
+
1331
+ ```javascript
1332
+ // Set specific field
1333
+ await client.json.set('user:1', '$.email', '"[email protected]"');
1334
+
1335
+ // Update nested field
1336
+ await client.json.set('user:1', '$.address.city', '"New York"');
1337
+
1338
+ // Delete field
1339
+ await client.json.del('user:1', '$.age');
1340
+ ```
1341
+
1342
+ ### JSON Array Operations
1343
+
1344
+ ```javascript
1345
+ // Append to array
1346
+ await client.json.arrAppend('user:1', '$.tags', '"nodejs"', '"javascript"');
1347
+
1348
+ // Get array length
1349
+ const length = await client.json.arrLen('user:1', '$.tags');
1350
+
1351
+ // Pop from array
1352
+ await client.json.arrPop('user:1', '$.tags', -1); // Pop last element
1353
+
1354
+ // Insert into array
1355
+ await client.json.arrInsert('user:1', '$.tags', 0, '"beginner"');
1356
+ ```
1357
+
1358
+ ### JSON Numeric Operations
1359
+
1360
+ ```javascript
1361
+ // Increment numeric value
1362
+ await client.json.numIncrBy('user:1', '$.loginCount', 1);
1363
+
1364
+ // Multiply numeric value
1365
+ await client.json.numMultBy('user:1', '$.score', 1.5);
1366
+ ```
1367
+
1368
+ ---
1369
+
1370
+ ## Time Series (RedisTimeSeries)
1371
+
1372
+ ### Basic Time Series Operations
1373
+
1374
+ ```javascript
1375
+ // Create time series
1376
+ await client.ts.create('temperature:sensor1', {
1377
+ RETENTION: 86400000, // Retain for 24 hours
1378
+ LABELS: { sensor: 'temp', location: 'room1' }
1379
+ });
1380
+
1381
+ // Add data points
1382
+ await client.ts.add('temperature:sensor1', '*', 22.5); // Current time
1383
+ await client.ts.add('temperature:sensor1', Date.now(), 23.0); // Specific time
1384
+
1385
+ // Add multiple data points
1386
+ await client.ts.mAdd([
1387
+ { key: 'temperature:sensor1', timestamp: '*', value: 22.8 },
1388
+ { key: 'temperature:sensor2', timestamp: '*', value: 21.5 }
1389
+ ]);
1390
+ ```
1391
+
1392
+ ### Query Time Series
1393
+
1394
+ ```javascript
1395
+ // Get range of data
1396
+ const data = await client.ts.range('temperature:sensor1', '-', '+');
1397
+
1398
+ // Get range with aggregation
1399
+ const hourlyAvg = await client.ts.range('temperature:sensor1', '-', '+', {
1400
+ AGGREGATION: {
1401
+ type: 'AVG',
1402
+ timeBucket: 3600000 // 1 hour
1403
+ }
1404
+ });
1405
+
1406
+ // Get last value
1407
+ const latest = await client.ts.get('temperature:sensor1');
1408
+ ```
1409
+
1410
+ ---
1411
+
1412
+ ## Search and Query (RediSearch)
1413
+
1414
+ ### Create Index
1415
+
1416
+ ```javascript
1417
+ // Create index on hash
1418
+ await client.ft.create('idx:users', {
1419
+ '$.name': {
1420
+ type: 'TEXT',
1421
+ AS: 'name'
1422
+ },
1423
+ '$.age': {
1424
+ type: 'NUMERIC',
1425
+ AS: 'age'
1426
+ },
1427
+ '$.email': {
1428
+ type: 'TAG',
1429
+ AS: 'email'
1430
+ }
1431
+ }, {
1432
+ ON: 'HASH',
1433
+ PREFIX: 'user:'
1434
+ });
1435
+ ```
1436
+
1437
+ ### Search Documents
1438
+
1439
+ ```javascript
1440
+ // Search by text
1441
+ const results = await client.ft.search('idx:users', 'John');
1442
+
1443
+ // Search with filters
1444
+ const filtered = await client.ft.search('idx:users', '@age:[25 35]');
1445
+
1446
+ // Search with multiple conditions
1447
+ const complex = await client.ft.search('idx:users', '@name:John @age:[20 40]');
1448
+
1449
+ // Get search count
1450
+ const count = await client.ft.search('idx:users', '*', { LIMIT: { from: 0, size: 0 } });
1451
+ ```
1452
+
1453
+ ---
1454
+
1455
+ ## Geospatial Operations
1456
+
1457
+ ### Add Geo Points
1458
+
1459
+ ```javascript
1460
+ // Add location
1461
+ await client.geoAdd('locations', {
1462
+ longitude: -122.4194,
1463
+ latitude: 37.7749,
1464
+ member: 'San Francisco'
1465
+ });
1466
+
1467
+ // Add multiple locations
1468
+ await client.geoAdd('locations', [
1469
+ { longitude: -118.2437, latitude: 34.0522, member: 'Los Angeles' },
1470
+ { longitude: -73.9352, latitude: 40.7306, member: 'New York' }
1471
+ ]);
1472
+ ```
1473
+
1474
+ ### Query Geo Points
1475
+
1476
+ ```javascript
1477
+ // Get position
1478
+ const position = await client.geoPos('locations', 'San Francisco');
1479
+ console.log(position); // [{ longitude: -122.4194, latitude: 37.7749 }]
1480
+
1481
+ // Get distance between points
1482
+ const distance = await client.geoDist('locations', 'San Francisco', 'Los Angeles', 'mi');
1483
+ console.log(`Distance: ${distance} miles`);
1484
+
1485
+ // Search by radius
1486
+ const nearby = await client.geoRadius('locations', {
1487
+ longitude: -122.4194,
1488
+ latitude: 37.7749
1489
+ }, 500, 'mi');
1490
+
1491
+ // Search by member
1492
+ const nearSF = await client.geoRadiusByMember('locations', 'San Francisco', 600, 'mi', {
1493
+ WITHDIST: true,
1494
+ WITHCOORD: true
1495
+ });
1496
+ ```
1497
+
1498
+ ---
1499
+
1500
+ ## HyperLogLog (Cardinality Estimation)
1501
+
1502
+ ```javascript
1503
+ // Add elements
1504
+ await client.pfAdd('unique:visitors', ['user1', 'user2', 'user3']);
1505
+ await client.pfAdd('unique:visitors', ['user2', 'user4']); // user2 counted once
1506
+
1507
+ // Get count
1508
+ const count = await client.pfCount('unique:visitors'); // ~4
1509
+
1510
+ // Merge multiple HyperLogLogs
1511
+ await client.pfMerge('combined', ['unique:visitors:day1', 'unique:visitors:day2']);
1512
+ ```
1513
+
1514
+ ---
1515
+
1516
+ ## Bitmap Operations
1517
+
1518
+ ```javascript
1519
+ // Set bit
1520
+ await client.setBit('login:2024-01-15', 100, 1); // User 100 logged in
1521
+
1522
+ // Get bit
1523
+ const bit = await client.getBit('login:2024-01-15', 100); // 1
1524
+
1525
+ // Count set bits
1526
+ const count = await client.bitCount('login:2024-01-15');
1527
+
1528
+ // Bitwise operations
1529
+ await client.bitOp('AND', 'result', ['bitmap1', 'bitmap2']);
1530
+ await client.bitOp('OR', 'result', ['bitmap1', 'bitmap2']);
1531
+ await client.bitOp('XOR', 'result', ['bitmap1', 'bitmap2']);
1532
+
1533
+ // Find first bit
1534
+ const pos = await client.bitPos('login:2024-01-15', 1); // First set bit
1535
+ ```
1536
+
1537
+ ---
1538
+
1539
+ ## Server and Connection Management
1540
+
1541
+ ### Server Information
1542
+
1543
+ ```javascript
1544
+ // Get server info
1545
+ const info = await client.info();
1546
+ console.log(info);
1547
+
1548
+ // Get specific section
1549
+ const stats = await client.info('stats');
1550
+ const memory = await client.info('memory');
1551
+
1552
+ // Ping server
1553
+ const pong = await client.ping(); // 'PONG'
1554
+
1555
+ // Get server time
1556
+ const time = await client.time();
1557
+ ```
1558
+
1559
+ ### Database Management
1560
+
1561
+ ```javascript
1562
+ // Select database (0-15 by default)
1563
+ await client.select(1);
1564
+
1565
+ // Get database size
1566
+ const size = await client.dbSize();
1567
+
1568
+ // Flush current database
1569
+ await client.flushDb();
1570
+
1571
+ // Flush all databases
1572
+ await client.flushAll();
1573
+
1574
+ // Save database to disk
1575
+ await client.save();
1576
+
1577
+ // Background save
1578
+ await client.bgSave();
1579
+
1580
+ // Get last save time
1581
+ const lastSave = await client.lastSave();
1582
+ ```
1583
+
1584
+ ### Client Management
1585
+
1586
+ ```javascript
1587
+ // Get client list
1588
+ const clients = await client.clientList();
1589
+
1590
+ // Get client ID
1591
+ const id = await client.clientId();
1592
+
1593
+ // Set client name
1594
+ await client.clientSetName('my-app');
1595
+
1596
+ // Get client name
1597
+ const name = await client.clientGetName();
1598
+
1599
+ // Get client info
1600
+ const clientInfo = await client.clientInfo();
1601
+ ```
1602
+
1603
+ ---
1604
+
1605
+ ## Error Handling
1606
+
1607
+ ### Connection Errors
1608
+
1609
+ ```javascript
1610
+ import { createClient } from 'redis';
1611
+
1612
+ const client = createClient({
1613
+ socket: {
1614
+ host: 'localhost',
1615
+ port: 6379,
1616
+ reconnectStrategy: (retries) => {
1617
+ if (retries > 10) {
1618
+ return new Error('Max retries reached');
1619
+ }
1620
+ return Math.min(retries * 100, 3000);
1621
+ }
1622
+ }
1623
+ });
1624
+
1625
+ client.on('error', (err) => {
1626
+ console.error('Redis error:', err);
1627
+ });
1628
+
1629
+ client.on('connect', () => {
1630
+ console.log('Connected to Redis');
1631
+ });
1632
+
1633
+ client.on('reconnecting', () => {
1634
+ console.log('Reconnecting to Redis...');
1635
+ });
1636
+
1637
+ client.on('ready', () => {
1638
+ console.log('Redis client ready');
1639
+ });
1640
+
1641
+ await client.connect();
1642
+ ```
1643
+
1644
+ ### Command Errors
1645
+
1646
+ ```javascript
1647
+ try {
1648
+ await client.get('nonexistent');
1649
+ } catch (error) {
1650
+ console.error('Command error:', error);
1651
+ }
1652
+
1653
+ // Multiple operations with individual error handling
1654
+ const results = await client.multi()
1655
+ .set('key1', 'value1')
1656
+ .incr('not-a-number') // This will error
1657
+ .get('key1')
1658
+ .exec();
1659
+
1660
+ results.forEach((result, i) => {
1661
+ if (result instanceof Error) {
1662
+ console.error(`Command ${i} failed:`, result.message);
1663
+ } else {
1664
+ console.log(`Command ${i} result:`, result);
1665
+ }
1666
+ });
1667
+ ```
1668
+
1669
+ ---
1670
+
1671
+ ## Connection Pooling and Performance
1672
+
1673
+ ### Configure Connection Pool
1674
+
1675
+ ```javascript
1676
+ const client = createClient({
1677
+ socket: {
1678
+ host: 'localhost',
1679
+ port: 6379,
1680
+ connectTimeout: 5000,
1681
+ keepAlive: 5000
1682
+ },
1683
+ password: 'your_password'
1684
+ });
1685
+ ```
1686
+
1687
+ ### Readonly Replicas
1688
+
1689
+ ```javascript
1690
+ const client = createClient({
1691
+ socket: {
1692
+ host: 'localhost',
1693
+ port: 6379
1694
+ }
1695
+ });
1696
+
1697
+ // Send read commands to replicas
1698
+ await client.sendCommand(['READONLY']);
1699
+ ```
1700
+
1701
+ ---
1702
+
1703
+ ## Cluster Support
1704
+
1705
+ ### Connect to Cluster
1706
+
1707
+ ```javascript
1708
+ import { createCluster } from 'redis';
1709
+
1710
+ const cluster = createCluster({
1711
+ rootNodes: [
1712
+ { url: 'redis://localhost:7000' },
1713
+ { url: 'redis://localhost:7001' },
1714
+ { url: 'redis://localhost:7002' }
1715
+ ]
1716
+ });
1717
+
1718
+ cluster.on('error', (err) => console.error('Cluster error:', err));
1719
+
1720
+ await cluster.connect();
1721
+
1722
+ // Use cluster like regular client
1723
+ await cluster.set('key', 'value');
1724
+ const value = await cluster.get('key');
1725
+
1726
+ await cluster.quit();
1727
+ ```
1728
+
1729
+ ---
1730
+
1731
+ ## Complete Application Example
1732
+
1733
+ ```javascript
1734
+ import { createClient } from 'redis';
1735
+ import dotenv from 'dotenv';
1736
+
1737
+ dotenv.config();
1738
+
1739
+ class RedisManager {
1740
+ constructor() {
1741
+ this.client = null;
1742
+ this.subscriber = null;
1743
+ }
1744
+
1745
+ async connect() {
1746
+ this.client = createClient({
1747
+ url: process.env.REDIS_URL || 'redis://localhost:6379',
1748
+ password: process.env.REDIS_PASSWORD
1749
+ });
1750
+
1751
+ this.client.on('error', (err) => {
1752
+ console.error('Redis Client Error:', err);
1753
+ });
1754
+
1755
+ await this.client.connect();
1756
+ console.log('Connected to Redis');
1757
+ }
1758
+
1759
+ async cacheGet(key) {
1760
+ const value = await this.client.get(key);
1761
+ return value ? JSON.parse(value) : null;
1762
+ }
1763
+
1764
+ async cacheSet(key, value, ttl = 3600) {
1765
+ await this.client.set(key, JSON.stringify(value), { EX: ttl });
1766
+ }
1767
+
1768
+ async invalidateCache(pattern) {
1769
+ const keys = [];
1770
+ for await (const key of this.client.scanIterator({ MATCH: pattern })) {
1771
+ keys.push(key);
1772
+ }
1773
+ if (keys.length > 0) {
1774
+ await this.client.del(keys);
1775
+ }
1776
+ }
1777
+
1778
+ async trackUserActivity(userId, action) {
1779
+ const key = `activity:${userId}:${new Date().toISOString().split('T')[0]}`;
1780
+ await this.client.rPush(key, JSON.stringify({
1781
+ action,
1782
+ timestamp: Date.now()
1783
+ }));
1784
+ await this.client.expire(key, 86400 * 7); // Keep for 7 days
1785
+ }
1786
+
1787
+ async getUserActivity(userId, date) {
1788
+ const key = `activity:${userId}:${date}`;
1789
+ const activities = await this.client.lRange(key, 0, -1);
1790
+ return activities.map(a => JSON.parse(a));
1791
+ }
1792
+
1793
+ async incrementPageView(pageId) {
1794
+ await this.client.incr(`pageviews:${pageId}`);
1795
+ }
1796
+
1797
+ async getPageViews(pageId) {
1798
+ const views = await this.client.get(`pageviews:${pageId}`);
1799
+ return parseInt(views || '0');
1800
+ }
1801
+
1802
+ async addToLeaderboard(userId, score) {
1803
+ await this.client.zAdd('leaderboard', { score, value: userId });
1804
+ }
1805
+
1806
+ async getLeaderboard(count = 10) {
1807
+ const results = await this.client.zRangeWithScores('leaderboard', 0, count - 1, {
1808
+ REV: true
1809
+ });
1810
+ return results.map(r => ({ userId: r.value, score: r.score }));
1811
+ }
1812
+
1813
+ async disconnect() {
1814
+ if (this.subscriber) {
1815
+ await this.subscriber.quit();
1816
+ }
1817
+ if (this.client) {
1818
+ await this.client.quit();
1819
+ }
1820
+ console.log('Disconnected from Redis');
1821
+ }
1822
+ }
1823
+
1824
+ // Usage
1825
+ const redis = new RedisManager();
1826
+ await redis.connect();
1827
+
1828
+ // Cache operations
1829
+ await redis.cacheSet('user:1000', { name: 'John', email: '[email protected]' });
1830
+ const user = await redis.cacheGet('user:1000');
1831
+ console.log(user);
1832
+
1833
+ // Track activity
1834
+ await redis.trackUserActivity('user:1000', 'login');
1835
+ await redis.trackUserActivity('user:1000', 'view_profile');
1836
+
1837
+ // Get activity
1838
+ const today = new Date().toISOString().split('T')[0];
1839
+ const activities = await redis.getUserActivity('user:1000', today);
1840
+ console.log(activities);
1841
+
1842
+ // Leaderboard
1843
+ await redis.addToLeaderboard('user:1000', 500);
1844
+ await redis.addToLeaderboard('user:2000', 750);
1845
+ await redis.addToLeaderboard('user:3000', 250);
1846
+
1847
+ const topUsers = await redis.getLeaderboard(3);
1848
+ console.log('Top users:', topUsers);
1849
+
1850
+ await redis.disconnect();
1851
+ ```