Skip to content

Commit 49d6b2d

Browse files
Update README.MD (#2924)
* Update README.MD * docs: update programmability.md examples + add Programmability section to README and * fix imports according to the new v5 exports * more v5 docs updates --------- Co-authored-by: Nikolay Karadzhov <nkaradzhov89@gmail.com>
1 parent 9459660 commit 49d6b2d

File tree

5 files changed

+341
-30
lines changed

5 files changed

+341
-30
lines changed

README.md

+253-6
Original file line numberDiff line numberDiff line change
@@ -27,24 +27,24 @@ node-redis is a modern, high performance [Redis](https://redis.io) client for No
2727

2828
## Installation
2929

30-
Start a redis-server via docker (or any other method you prefer):
30+
Start a redis via docker:
3131

3232
```bash
33-
docker run -p 6379:6379 -it redis/redis-stack-server:latest
33+
docker run -p 6379:6379 -d redis:8.0-rc1
3434
```
3535

3636
To install node-redis, simply:
3737

3838
```bash
3939
npm install redis
4040
```
41-
42-
> "redis" is the "whole in one" package that includes all the other packages. If you only need a subset of the commands, you can install the individual packages. See the list below.
41+
> "redis" is the "whole in one" package that includes all the other packages. If you only need a subset of the commands,
42+
> you can install the individual packages. See the list below.
4343
4444
## Packages
4545

4646
| Name | Description |
47-
|------------------------------------------------|---------------------------------------------------------------------------------------------|
47+
| ---------------------------------------------- | ------------------------------------------------------------------------------------------- |
4848
| [`redis`](./packages/redis) | The client with all the ["redis-stack"](https://github.com/redis-stack/redis-stack) modules |
4949
| [`@redis/client`](./packages/client) | The base clients (i.e `RedisClient`, `RedisCluster`, etc.) |
5050
| [`@redis/bloom`](./packages/bloom) | [Redis Bloom](https://redis.io/docs/data-types/probabilistic/) commands |
@@ -53,7 +53,254 @@ npm install redis
5353
| [`@redis/time-series`](./packages/time-series) | [Redis Time-Series](https://redis.io/docs/data-types/timeseries/) commands |
5454
| [`@redis/entraid`](./packages/entraid) | Secure token-based authentication for Redis clients using Microsoft Entra ID |
5555

56-
> Looking for a high-level library to handle object mapping? See [redis-om-node](https://github.com/redis/redis-om-node)!
56+
> Looking for a high-level library to handle object mapping?
57+
> See [redis-om-node](https://github.com/redis/redis-om-node)!
58+
59+
60+
## Usage
61+
62+
### Basic Example
63+
64+
```typescript
65+
import { createClient } from "redis";
66+
67+
const client = await createClient()
68+
.on("error", (err) => console.log("Redis Client Error", err))
69+
.connect();
70+
71+
await client.set("key", "value");
72+
const value = await client.get("key");
73+
client.destroy();
74+
```
75+
76+
The above code connects to localhost on port 6379. To connect to a different host or port, use a connection string in
77+
the format `redis[s]://[[username][:password]@][host][:port][/db-number]`:
78+
79+
```typescript
80+
createClient({
81+
url: "redis://alice:foobared@awesome.redis.server:6380",
82+
});
83+
```
84+
85+
You can also use discrete parameters, UNIX sockets, and even TLS to connect. Details can be found in
86+
the [client configuration guide](./docs/client-configuration.md).
87+
88+
To check if the the client is connected and ready to send commands, use `client.isReady` which returns a boolean.
89+
`client.isOpen` is also available. This returns `true` when the client's underlying socket is open, and `false` when it
90+
isn't (for example when the client is still connecting or reconnecting after a network error).
91+
92+
### Redis Commands
93+
94+
There is built-in support for all of the [out-of-the-box Redis commands](https://redis.io/commands). They are exposed
95+
using the raw Redis command names (`HSET`, `HGETALL`, etc.) and a friendlier camel-cased version (`hSet`, `hGetAll`,
96+
etc.):
97+
98+
```typescript
99+
// raw Redis commands
100+
await client.HSET("key", "field", "value");
101+
await client.HGETALL("key");
102+
103+
// friendly JavaScript commands
104+
await client.hSet("key", "field", "value");
105+
await client.hGetAll("key");
106+
```
107+
108+
Modifiers to commands are specified using a JavaScript object:
109+
110+
```typescript
111+
await client.set("key", "value", {
112+
EX: 10,
113+
NX: true,
114+
});
115+
```
116+
117+
Replies will be transformed into useful data structures:
118+
119+
```typescript
120+
await client.hGetAll("key"); // { field1: 'value1', field2: 'value2' }
121+
await client.hVals("key"); // ['value1', 'value2']
122+
```
123+
124+
`Buffer`s are supported as well:
125+
126+
```typescript
127+
const client = createClient().withTypeMapping({
128+
[RESP_TYPES.BLOB_STRING]: Buffer
129+
});
130+
131+
await client.hSet("key", "field", Buffer.from("value")); // 'OK'
132+
await client.hGet("key", "field"); // { field: <Buffer 76 61 6c 75 65> }
133+
134+
```
135+
136+
### Unsupported Redis Commands
137+
138+
If you want to run commands and/or use arguments that Node Redis doesn't know about (yet!) use `.sendCommand()`:
139+
140+
```typescript
141+
await client.sendCommand(["SET", "key", "value", "NX"]); // 'OK'
142+
143+
await client.sendCommand(["HGETALL", "key"]); // ['key1', 'field1', 'key2', 'field2']
144+
```
145+
146+
### Transactions (Multi/Exec)
147+
148+
Start a [transaction](https://redis.io/topics/transactions) by calling `.multi()`, then chaining your commands. When
149+
you're done, call `.exec()` and you'll get an array back with your results:
150+
151+
```typescript
152+
await client.set("another-key", "another-value");
153+
154+
const [setKeyReply, otherKeyValue] = await client
155+
.multi()
156+
.set("key", "value")
157+
.get("another-key")
158+
.exec(); // ['OK', 'another-value']
159+
```
160+
161+
You can also [watch](https://redis.io/topics/transactions#optimistic-locking-using-check-and-set) keys by calling
162+
`.watch()`. Your transaction will abort if any of the watched keys change.
163+
164+
165+
### Blocking Commands
166+
167+
In v4, `RedisClient` had the ability to create a pool of connections using an "Isolation Pool" on top of the "main"
168+
connection. However, there was no way to use the pool without a "main" connection:
169+
170+
```javascript
171+
const client = await createClient()
172+
.on("error", (err) => console.error(err))
173+
.connect();
174+
175+
await client.ping(client.commandOptions({ isolated: true }));
176+
```
177+
178+
In v5 we've extracted this pool logic into its own class—`RedisClientPool`:
179+
180+
```javascript
181+
const pool = await createClientPool()
182+
.on("error", (err) => console.error(err))
183+
.connect();
184+
185+
await pool.ping();
186+
```
187+
188+
189+
### Pub/Sub
190+
191+
See the [Pub/Sub overview](./docs/pub-sub.md).
192+
193+
### Scan Iterator
194+
195+
[`SCAN`](https://redis.io/commands/scan) results can be looped over
196+
using [async iterators](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/asyncIterator):
197+
198+
```typescript
199+
for await (const key of client.scanIterator()) {
200+
// use the key!
201+
await client.get(key);
202+
}
203+
```
204+
205+
This works with `HSCAN`, `SSCAN`, and `ZSCAN` too:
206+
207+
```typescript
208+
for await (const { field, value } of client.hScanIterator("hash")) {
209+
}
210+
for await (const member of client.sScanIterator("set")) {
211+
}
212+
for await (const { score, value } of client.zScanIterator("sorted-set")) {
213+
}
214+
```
215+
216+
You can override the default options by providing a configuration object:
217+
218+
```typescript
219+
client.scanIterator({
220+
TYPE: "string", // `SCAN` only
221+
MATCH: "patter*",
222+
COUNT: 100,
223+
});
224+
```
225+
226+
### Disconnecting
227+
228+
The `QUIT` command has been deprecated in Redis 7.2 and should now also be considered deprecated in Node-Redis. Instead
229+
of sending a `QUIT` command to the server, the client can simply close the network connection.
230+
231+
`client.QUIT/quit()` is replaced by `client.close()`. and, to avoid confusion, `client.disconnect()` has been renamed to
232+
`client.destroy()`.
233+
234+
```typescript
235+
client.destroy();
236+
```
237+
238+
### Auto-Pipelining
239+
240+
Node Redis will automatically pipeline requests that are made during the same "tick".
241+
242+
```typescript
243+
client.set("Tm9kZSBSZWRpcw==", "users:1");
244+
client.sAdd("users:1:tokens", "Tm9kZSBSZWRpcw==");
245+
```
246+
247+
Of course, if you don't do something with your Promises you're certain to
248+
get [unhandled Promise exceptions](https://nodejs.org/api/process.html#process_event_unhandledrejection). To take
249+
advantage of auto-pipelining and handle your Promises, use `Promise.all()`.
250+
251+
```typescript
252+
await Promise.all([
253+
client.set("Tm9kZSBSZWRpcw==", "users:1"),
254+
client.sAdd("users:1:tokens", "Tm9kZSBSZWRpcw=="),
255+
]);
256+
```
257+
258+
### Programmability
259+
260+
See the [Programmability overview](./docs/programmability.md).
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+
### Events
267+
268+
The Node Redis client class is an Nodejs EventEmitter and it emits an event each time the network status changes:
269+
270+
| Name | When | Listener arguments |
271+
| ----------------------- | ---------------------------------------------------------------------------------- | --------------------------------------------------------- |
272+
| `connect` | Initiating a connection to the server | _No arguments_ |
273+
| `ready` | Client is ready to use | _No arguments_ |
274+
| `end` | Connection has been closed (via `.disconnect()`) | _No arguments_ |
275+
| `error` | An error has occurred—usually a network issue such as "Socket closed unexpectedly" | `(error: Error)` |
276+
| `reconnecting` | Client is trying to reconnect to the server | _No arguments_ |
277+
| `sharded-channel-moved` | See [here](./docs/pub-sub.md#sharded-channel-moved-event) | See [here](./docs/pub-sub.md#sharded-channel-moved-event) |
278+
279+
> :warning: You **MUST** listen to `error` events. If a client doesn't have at least one `error` listener registered and
280+
> an `error` occurs, that error will be thrown and the Node.js process will exit. See the [ > `EventEmitter` docs](https://nodejs.org/api/events.html#events_error_events) for more details.
281+
282+
> The client will not emit [any other events](./docs/v3-to-v4.md#all-the-removed-events) beyond those listed above.
283+
284+
## Supported Redis versions
285+
286+
Node Redis is supported with the following versions of Redis:
287+
288+
| Version | Supported |
289+
| ------- | ------------------ |
290+
| 8.0.z | :heavy_check_mark: |
291+
| 7.0.z | :heavy_check_mark: |
292+
| 6.2.z | :heavy_check_mark: |
293+
| 6.0.z | :heavy_check_mark: |
294+
| 5.0.z | :heavy_check_mark: |
295+
| < 5.0 | :x: |
296+
297+
> Node Redis should work with older versions of Redis, but it is not fully tested and we cannot offer support.
298+
299+
## Migration
300+
301+
- [From V3 to V4](docs/v3-to-v4.md)
302+
- [From V4 to V5](docs/v4-to-v5.md)
303+
- [V5](docs/v5.md)
57304

58305
## Contributing
59306

docs/RESP.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ By default, each type is mapped to the first option in the lists below. To chang
2323
- Double (`,`) => `number | string`
2424
- Simple String (`+`) => `string | Buffer`
2525
- Blob String (`$`) => `string | Buffer`
26-
- Verbatim String (`=`) => `string | Buffer | VerbatimString` (TODO: `VerbatimString` typedoc link)
26+
- Verbatim String (`=`) => `string | Buffer | VerbatimString`
2727
- Simple Error (`-`) => `ErrorReply`
2828
- Blob Error (`!`) => `ErrorReply`
2929
- Array (`*`) => `Array`

docs/programmability.md

+22-11
Original file line numberDiff line numberDiff line change
@@ -25,16 +25,22 @@ FUNCTION LOAD "#!lua name=library\nredis.register_function{function_name='add',
2525
Load the prior redis function on the _redis server_ before running the example below.
2626

2727
```typescript
28-
import { createClient } from 'redis';
28+
import { CommandParser } from '@redis/client/lib/client/parser';
29+
import { NumberReply } from '@redis/client/lib/RESP/types';
30+
import { createClient, RedisArgument } from 'redis';
2931

3032
const client = createClient({
3133
functions: {
3234
library: {
3335
add: {
3436
NUMBER_OF_KEYS: 1,
35-
FIRST_KEY_INDEX: 1,
36-
transformArguments(key: string, toAdd: number): Array<string> {
37-
return [key, toAdd.toString()];
37+
parseCommand(
38+
parser: CommandParser,
39+
key: RedisArgument,
40+
toAdd: RedisArgument
41+
) {
42+
parser.pushKey(key)
43+
parser.push(toAdd)
3844
},
3945
transformReply: undefined as unknown as () => NumberReply
4046
}
@@ -43,34 +49,39 @@ const client = createClient({
4349
});
4450

4551
await client.connect();
46-
4752
await client.set('key', '1');
48-
await client.library.add('key', 2); // 3
53+
await client.library.add('key', '2'); // 3
4954
```
5055

5156
## [Lua Scripts](https://redis.io/docs/manual/programmability/eval-intro/)
5257

5358
The following is an end-to-end example of the prior concept.
5459

5560
```typescript
56-
import { createClient, defineScript, NumberReply } from 'redis';
61+
import { CommandParser } from '@redis/client/lib/client/parser';
62+
import { NumberReply } from '@redis/client/lib/RESP/types';
63+
import { createClient, defineScript, RedisArgument } from 'redis';
5764

5865
const client = createClient({
5966
scripts: {
6067
add: defineScript({
6168
SCRIPT: 'return redis.call("GET", KEYS[1]) + ARGV[1];',
6269
NUMBER_OF_KEYS: 1,
6370
FIRST_KEY_INDEX: 1,
64-
transformArguments(key: string, toAdd: number): Array<string> {
65-
return [key, toAdd.toString()];
71+
parseCommand(
72+
parser: CommandParser,
73+
key: RedisArgument,
74+
toAdd: RedisArgument
75+
) {
76+
parser.pushKey(key)
77+
parser.push(toAdd)
6678
},
6779
transformReply: undefined as unknown as () => NumberReply
6880
})
6981
}
7082
});
7183

7284
await client.connect();
73-
7485
await client.set('key', '1');
75-
await client.add('key', 2); // 3
86+
await client.add('key', '2'); // 3
7687
```

docs/v4-to-v5.md

+6-6
Original file line numberDiff line numberDiff line change
@@ -234,12 +234,12 @@ In v5, any unwritten commands (in the same pipeline) will be discarded.
234234
### Time Series
235235

236236
- `TS.ADD`: `boolean` -> `number` [^boolean-to-number]
237-
- `TS.[M][REV]RANGE`: `enum TimeSeriesBucketTimestamp` -> `const TIME_SERIES_BUCKET_TIMESTAMP` [^enum-to-constants], `enum TimeSeriesReducers` -> `const TIME_SERIES_REDUCERS` [^enum-to-constants], the `ALIGN` argument has been moved into `AGGREGRATION`
237+
- `TS.[M][REV]RANGE`: the `ALIGN` argument has been moved into `AGGREGATION`
238238
- `TS.SYNUPDATE`: `Array<string | Array<string>>` -> `Record<string, Array<string>>`
239-
- `TS.M[REV]RANGE[_WITHLABELS]`, `TS.MGET[_WITHLABELS]`: TODO
240-
241-
[^enum-to-constants]: TODO
239+
- `TimeSeriesDuplicatePolicies` -> `TIME_SERIES_DUPLICATE_POLICIES` [^enum-to-constants]
240+
- `TimeSeriesEncoding` -> `TIME_SERIES_ENCODING` [^enum-to-constants]
241+
- `TimeSeriesAggregationType` -> `TIME_SERIES_AGGREGATION_TYPE` [^enum-to-constants]
242+
- `TimeSeriesReducers` -> `TIME_SERIES_REDUCERS` [^enum-to-constants]
243+
- `TimeSeriesBucketTimestamp` -> `TIME_SERIES_BUCKET_TIMESTAMP` [^enum-to-constants]
242244

243245
[^map-keys]: To avoid unnecessary transformations and confusion, map keys will not be transformed to "js friendly" names (i.e. `number-of-keys` will not be renamed to `numberOfKeys`). See [here](https://github.com/redis/node-redis/discussions/2506).
244-
245-
[^future-proofing]: TODO

0 commit comments

Comments
 (0)