|
| 1 | +# Node-Redis |
| 2 | + |
| 3 | +[](https://codecov.io/gh/redis/node-redis) |
| 4 | +[](https://codecov.io/gh/redis/node-redis) |
| 5 | +[](https://codecov.io/gh/redis/node-redis) |
| 6 | +[](https://discord.gg/XMMVgxUm) |
| 7 | + |
| 8 | +## Installation |
| 9 | + |
| 10 | +```bash |
| 11 | +npm install redis@next |
| 12 | +``` |
| 13 | + |
| 14 | +> :warning: The new interface is clean and cool, but if you have an existing code base, you'll want to read the [migration guide](../docs/v3-to-v4.md). |
| 15 | +
|
| 16 | +## Usage |
| 17 | + |
| 18 | +### Basic Example |
| 19 | + |
| 20 | +```typescript |
| 21 | +import { createClient } from 'redis'; |
| 22 | + |
| 23 | +(async () => { |
| 24 | + const client = createClient(); |
| 25 | + |
| 26 | + client.on('error', (err) => console.log('Redis Client Error', err)); |
| 27 | + |
| 28 | + await client.connect(); |
| 29 | + |
| 30 | + await client.set('key', 'value'); |
| 31 | + const value = await client.get('key'); |
| 32 | +})(); |
| 33 | +``` |
| 34 | + |
| 35 | +The above code connects to localhost on port 6379. To connect to a different host or port, use a connection string in the format `redis[s]://[[username][:password]@][host][:port][/db-number]`: |
| 36 | + |
| 37 | +```typescript |
| 38 | +createClient({ |
| 39 | + url: 'redis://alice:[email protected]:6380' |
| 40 | +}); |
| 41 | +``` |
| 42 | + |
| 43 | +You can also use discrete parameters, UNIX sockets, and even TLS to connect. Details can be found in the [client configuration guide](../docs/client-configuration.md). |
| 44 | + |
| 45 | +### Redis Commands |
| 46 | + |
| 47 | +There is built-in support for all of the [out-of-the-box Redis commands](https://redis.io/commands). They are exposed using the raw Redis command names (`HSET`, `HGETALL`, etc.) and a friendlier camel-cased version (`hSet`, `hGetAll`, etc.): |
| 48 | + |
| 49 | +```typescript |
| 50 | +// raw Redis commands |
| 51 | +await client.HSET('key', 'field', 'value'); |
| 52 | +await client.HGETALL('key'); |
| 53 | + |
| 54 | +// friendly JavaScript commands |
| 55 | +await client.hSet('key', 'field', 'value'); |
| 56 | +await client.hGetAll('key'); |
| 57 | +``` |
| 58 | + |
| 59 | +Modifiers to commands are specified using a JavaScript object: |
| 60 | + |
| 61 | +```typescript |
| 62 | +await client.set('key', 'value', { |
| 63 | + EX: 10, |
| 64 | + NX: true |
| 65 | +}); |
| 66 | +``` |
| 67 | + |
| 68 | +Replies will be transformed into useful data structures: |
| 69 | + |
| 70 | +```typescript |
| 71 | +await client.hGetAll('key'); // { field1: 'value1', field2: 'value2' } |
| 72 | +await client.hVals('key'); // ['value1', 'value2'] |
| 73 | +``` |
| 74 | + |
| 75 | +### Unsupported Redis Commands |
| 76 | + |
| 77 | +If you want to run commands and/or use arguments that Node Redis doesn't know about (yet!) use `.sendCommand()`: |
| 78 | + |
| 79 | +```typescript |
| 80 | +await client.sendCommand(['SET', 'key', 'value', 'NX']); // 'OK' |
| 81 | + |
| 82 | +await client.sendCommand(['HGETALL', 'key']); // ['key1', 'field1', 'key2', 'field2'] |
| 83 | +``` |
| 84 | + |
| 85 | +### Transactions (Multi/Exec) |
| 86 | + |
| 87 | +Start a [transaction](https://redis.io/topics/transactions) by calling `.multi()`, then chaining your commands. When you're done, call `.exec()` and you'll get an array back with your results: |
| 88 | + |
| 89 | +```typescript |
| 90 | +await client.set('another-key', 'another-value'); |
| 91 | + |
| 92 | +const [setKeyReply, otherKeyValue] = await client |
| 93 | + .multi() |
| 94 | + .set('key', 'value') |
| 95 | + .get('another-key') |
| 96 | + .exec(); // ['OK', 'another-value'] |
| 97 | +``` |
| 98 | + |
| 99 | +You can also [watch](https://redis.io/topics/transactions#optimistic-locking-using-check-and-set) keys by calling `.watch()`. Your transaction will abort if any of the watched keys change. |
| 100 | + |
| 101 | +To dig deeper into transactions, check out the [Isolated Execution Guide](../docs/isolated-execution.md). |
| 102 | + |
| 103 | +### Blocking Commands |
| 104 | + |
| 105 | +Any command can be run on a new connection by specifying the `isolated` option. The newly created connection is closed when the command's `Promise` is fulfilled. |
| 106 | + |
| 107 | +This pattern works especially well for blocking commands—such as `BLPOP` and `BLMOVE`: |
| 108 | + |
| 109 | +```typescript |
| 110 | +import { commandOptions } from 'redis'; |
| 111 | + |
| 112 | +const blPopPromise = client.blPop(commandOptions({ isolated: true }), 'key', 0); |
| 113 | + |
| 114 | +await client.lPush('key', ['1', '2']); |
| 115 | + |
| 116 | +await blPopPromise; // '2' |
| 117 | +``` |
| 118 | + |
| 119 | +To learn more about isolated execution, check out the [guide](../docs/isolated-execution.md). |
| 120 | + |
| 121 | +### Pub/Sub |
| 122 | + |
| 123 | +Subscribing to a channel requires a dedicated stand-alone connection. You can easily get one by `.duplicate()`ing an existing Redis connection. |
| 124 | + |
| 125 | +```typescript |
| 126 | +const subscriber = client.duplicate(); |
| 127 | + |
| 128 | +await subscriber.connect(); |
| 129 | +``` |
| 130 | + |
| 131 | +Once you have one, simply subscribe and unsubscribe as needed: |
| 132 | + |
| 133 | +```typescript |
| 134 | +await subscriber.subscribe('channel', (message) => { |
| 135 | + console.log(message); // 'message' |
| 136 | +}); |
| 137 | + |
| 138 | +await subscriber.pSubscribe('channe*', (message, channel) => { |
| 139 | + console.log(message, channel); // 'message', 'channel' |
| 140 | +}); |
| 141 | + |
| 142 | +await subscriber.unsubscribe('channel'); |
| 143 | + |
| 144 | +await subscriber.pUnsubscribe('channe*'); |
| 145 | +``` |
| 146 | + |
| 147 | +Publish a message on a channel: |
| 148 | + |
| 149 | +```typescript |
| 150 | +await publisher.publish('channel', 'message'); |
| 151 | +``` |
| 152 | + |
| 153 | +### Scan Iterator |
| 154 | + |
| 155 | +[`SCAN`](https://redis.io/commands/scan) results can be looped over using [async iterators](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/asyncIterator): |
| 156 | + |
| 157 | +```typescript |
| 158 | +for await (const key of client.scanIterator()) { |
| 159 | + // use the key! |
| 160 | + await client.get(key); |
| 161 | +} |
| 162 | +``` |
| 163 | + |
| 164 | +This works with `HSCAN`, `SSCAN`, and `ZSCAN` too: |
| 165 | + |
| 166 | +```typescript |
| 167 | +for await (const { field, value } of client.hScanIterator('hash')) {} |
| 168 | +for await (const member of client.sScanIterator('set')) {} |
| 169 | +for await (const { score, member } of client.zScanIterator('sorted-set')) {} |
| 170 | +``` |
| 171 | + |
| 172 | +You can override the default options by providing a configuration object: |
| 173 | + |
| 174 | +```typescript |
| 175 | +client.scanIterator({ |
| 176 | + TYPE: 'string', // `SCAN` only |
| 177 | + MATCH: 'patter*', |
| 178 | + COUNT: 100 |
| 179 | +}); |
| 180 | +``` |
| 181 | + |
| 182 | +### Lua Scripts |
| 183 | + |
| 184 | +Define new functions using [Lua scripts](https://redis.io/commands/eval) which execute on the Redis server: |
| 185 | + |
| 186 | +```typescript |
| 187 | +import { createClient, defineScript } from 'redis'; |
| 188 | + |
| 189 | +(async () => { |
| 190 | + const client = createClient({ |
| 191 | + scripts: { |
| 192 | + add: defineScript({ |
| 193 | + NUMBER_OF_KEYS: 1, |
| 194 | + SCRIPT: |
| 195 | + 'local val = redis.pcall("GET", KEYS[1]);' + |
| 196 | + 'return val + ARGV[1];', |
| 197 | + transformArguments(key: string, toAdd: number): Array<string> { |
| 198 | + return [key, toAdd.toString()]; |
| 199 | + }, |
| 200 | + transformReply(reply: number): number { |
| 201 | + return reply; |
| 202 | + } |
| 203 | + }) |
| 204 | + } |
| 205 | + }); |
| 206 | + |
| 207 | + await client.connect(); |
| 208 | + |
| 209 | + await client.set('key', '1'); |
| 210 | + await client.add('key', 2); // 3 |
| 211 | +})(); |
| 212 | +``` |
| 213 | + |
| 214 | +### Disconnecting |
| 215 | + |
| 216 | +There are two functions that disconnect a client from the Redis server. In most scenarios you should use `.quit()` to ensure that pending commands are sent to Redis before closing a connection. |
| 217 | + |
| 218 | +#### `.QUIT()`/`.quit()` |
| 219 | + |
| 220 | +Gracefully close a client's connection to Redis, by sending the [`QUIT`](https://redis.io/commands/quit) command to the server. Before quitting, the client executes any remaining commands in its queue, and will receive replies from Redis for each of them. |
| 221 | + |
| 222 | +```typescript |
| 223 | +const [ping, get, quit] = await Promise.all([ |
| 224 | + client.ping(), |
| 225 | + client.get('key'), |
| 226 | + client.quit() |
| 227 | +]); // ['PONG', null, 'OK'] |
| 228 | + |
| 229 | +try { |
| 230 | + await client.get('key'); |
| 231 | +} catch (err) { |
| 232 | + // ClosedClient Error |
| 233 | +} |
| 234 | +``` |
| 235 | + |
| 236 | +#### `.disconnect()` |
| 237 | + |
| 238 | +Forcibly close a client's connection to Redis immediately. Calling `disconnect` will not send further pending commands to the Redis server, or wait for or parse outstanding responses. |
| 239 | + |
| 240 | +```typescript |
| 241 | +await client.disconnect(); |
| 242 | +``` |
| 243 | + |
| 244 | +### Auto-Pipelining |
| 245 | + |
| 246 | +Node Redis will automatically pipeline requests that are made during the same "tick". |
| 247 | + |
| 248 | +```typescript |
| 249 | +client.set('Tm9kZSBSZWRpcw==', 'users:1'); |
| 250 | +client.sAdd('users:1:tokens', 'Tm9kZSBSZWRpcw=='); |
| 251 | +``` |
| 252 | + |
| 253 | +Of course, if you don't do something with your Promises you're certain to get [unhandled Promise exceptions](https://nodejs.org/api/process.html#process_event_unhandledrejection). To take advantage of auto-pipelining and handle your Promises, use `Promise.all()`. |
| 254 | + |
| 255 | +```typescript |
| 256 | +await Promise.all([ |
| 257 | + client.set('Tm9kZSBSZWRpcw==', 'users:1'), |
| 258 | + client.sAdd('users:1:tokens', 'Tm9kZSBSZWRpcw==') |
| 259 | +]); |
| 260 | +``` |
| 261 | + |
| 262 | +### Clustering |
| 263 | + |
| 264 | +Check out the [Clustering Guide](../docs/clustering.md) when using Node Redis to connect to a Redis Cluster. |
| 265 | + |
| 266 | +## Supported Redis versions |
| 267 | + |
| 268 | +Node Redis is supported with the following versions of Redis: |
| 269 | + |
| 270 | +| Version | Supported | |
| 271 | +|---------|--------------------| |
| 272 | +| 6.2.z | :heavy_check_mark: | |
| 273 | +| 6.0.z | :heavy_check_mark: | |
| 274 | +| 5.y.z | :heavy_check_mark: | |
| 275 | +| < 5.0 | :x: | |
| 276 | + |
| 277 | +> Node Redis should work with older versions of Redis, but it is not fully tested and we cannot offer support. |
| 278 | +
|
| 279 | +## Packages |
| 280 | + |
| 281 | +| Name | Description | |
| 282 | +|-------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| |
| 283 | +| [redis](../) | [](https://www.npmjs.com/package/redis/v/next) [](https://www.npmjs.com/package/redis/v/next) | |
| 284 | +| [@node-redis/client](../packages/client) | [](https://www.npmjs.com/package/@node-redis/client/v/next) [](https://www.npmjs.com/package/@node-redis/client/v/next) | |
| 285 | +| [@node-redis/json](../packages/json) | [](https://www.npmjs.com/package/@node-redis/json/v/next) [](https://www.npmjs.com/package/@node-redis/json/v/next) [Redis JSON](https://oss.redis.com/redisjson/) commands | |
| 286 | +| [@node-redis/search](../packages/search) | [](https://www.npmjs.com/package/@node-redis/search/v/next) [](https://www.npmjs.com/package/@node-redis/search/v/next) [Redis Search](https://oss.redis.com/redisearch/) commands | |
| 287 | + |
| 288 | +## Contributing |
| 289 | + |
| 290 | +If you'd like to contribute, check out the [contributing guide](CONTRIBUTING.md). |
| 291 | + |
| 292 | +Thank you to all the people who already contributed to Node Redis! |
| 293 | + |
| 294 | +[](https://github.com/redis/node-redis/graphs/contributors) |
| 295 | + |
| 296 | +## License |
| 297 | + |
| 298 | +This repository is licensed under the "MIT" license. See [LICENSE](LICENSE). |
0 commit comments