1
0
mirror of https://github.com/redis/node-redis.git synced 2025-08-09 00:22:08 +03:00

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>
This commit is contained in:
Bobby I.
2025-04-30 16:28:22 +03:00
committed by GitHub
parent 9459660d96
commit 49d6b2d465
5 changed files with 341 additions and 30 deletions

259
README.md
View File

@@ -27,10 +27,10 @@ node-redis is a modern, high performance [Redis](https://redis.io) client for No
## Installation ## Installation
Start a redis-server via docker (or any other method you prefer): Start a redis via docker:
```bash ```bash
docker run -p 6379:6379 -it redis/redis-stack-server:latest docker run -p 6379:6379 -d redis:8.0-rc1
``` ```
To install node-redis, simply: To install node-redis, simply:
@@ -38,13 +38,13 @@ To install node-redis, simply:
```bash ```bash
npm install redis npm install redis
``` ```
> "redis" is the "whole in one" package that includes all the other packages. If you only need a subset of the commands,
> "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. > you can install the individual packages. See the list below.
## Packages ## Packages
| Name | Description | | Name | Description |
|------------------------------------------------|---------------------------------------------------------------------------------------------| | ---------------------------------------------- | ------------------------------------------------------------------------------------------- |
| [`redis`](./packages/redis) | The client with all the ["redis-stack"](https://github.com/redis-stack/redis-stack) modules | | [`redis`](./packages/redis) | The client with all the ["redis-stack"](https://github.com/redis-stack/redis-stack) modules |
| [`@redis/client`](./packages/client) | The base clients (i.e `RedisClient`, `RedisCluster`, etc.) | | [`@redis/client`](./packages/client) | The base clients (i.e `RedisClient`, `RedisCluster`, etc.) |
| [`@redis/bloom`](./packages/bloom) | [Redis Bloom](https://redis.io/docs/data-types/probabilistic/) commands | | [`@redis/bloom`](./packages/bloom) | [Redis Bloom](https://redis.io/docs/data-types/probabilistic/) commands |
@@ -53,7 +53,254 @@ npm install redis
| [`@redis/time-series`](./packages/time-series) | [Redis Time-Series](https://redis.io/docs/data-types/timeseries/) commands | | [`@redis/time-series`](./packages/time-series) | [Redis Time-Series](https://redis.io/docs/data-types/timeseries/) commands |
| [`@redis/entraid`](./packages/entraid) | Secure token-based authentication for Redis clients using Microsoft Entra ID | | [`@redis/entraid`](./packages/entraid) | Secure token-based authentication for Redis clients using Microsoft Entra ID |
> Looking for a high-level library to handle object mapping? See [redis-om-node](https://github.com/redis/redis-om-node)! > Looking for a high-level library to handle object mapping?
> See [redis-om-node](https://github.com/redis/redis-om-node)!
## Usage
### Basic Example
```typescript
import { createClient } from "redis";
const client = await createClient()
.on("error", (err) => console.log("Redis Client Error", err))
.connect();
await client.set("key", "value");
const value = await client.get("key");
client.destroy();
```
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]`:
```typescript
createClient({
url: "redis://alice:foobared@awesome.redis.server:6380",
});
```
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).
To check if the the client is connected and ready to send commands, use `client.isReady` which returns a boolean.
`client.isOpen` is also available. This returns `true` when the client's underlying socket is open, and `false` when it
isn't (for example when the client is still connecting or reconnecting after a network error).
### Redis Commands
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.):
```typescript
// raw Redis commands
await client.HSET("key", "field", "value");
await client.HGETALL("key");
// friendly JavaScript commands
await client.hSet("key", "field", "value");
await client.hGetAll("key");
```
Modifiers to commands are specified using a JavaScript object:
```typescript
await client.set("key", "value", {
EX: 10,
NX: true,
});
```
Replies will be transformed into useful data structures:
```typescript
await client.hGetAll("key"); // { field1: 'value1', field2: 'value2' }
await client.hVals("key"); // ['value1', 'value2']
```
`Buffer`s are supported as well:
```typescript
const client = createClient().withTypeMapping({
[RESP_TYPES.BLOB_STRING]: Buffer
});
await client.hSet("key", "field", Buffer.from("value")); // 'OK'
await client.hGet("key", "field"); // { field: <Buffer 76 61 6c 75 65> }
```
### Unsupported Redis Commands
If you want to run commands and/or use arguments that Node Redis doesn't know about (yet!) use `.sendCommand()`:
```typescript
await client.sendCommand(["SET", "key", "value", "NX"]); // 'OK'
await client.sendCommand(["HGETALL", "key"]); // ['key1', 'field1', 'key2', 'field2']
```
### Transactions (Multi/Exec)
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:
```typescript
await client.set("another-key", "another-value");
const [setKeyReply, otherKeyValue] = await client
.multi()
.set("key", "value")
.get("another-key")
.exec(); // ['OK', 'another-value']
```
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.
### Blocking Commands
In v4, `RedisClient` had the ability to create a pool of connections using an "Isolation Pool" on top of the "main"
connection. However, there was no way to use the pool without a "main" connection:
```javascript
const client = await createClient()
.on("error", (err) => console.error(err))
.connect();
await client.ping(client.commandOptions({ isolated: true }));
```
In v5 we've extracted this pool logic into its own class—`RedisClientPool`:
```javascript
const pool = await createClientPool()
.on("error", (err) => console.error(err))
.connect();
await pool.ping();
```
### Pub/Sub
See the [Pub/Sub overview](./docs/pub-sub.md).
### Scan Iterator
[`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):
```typescript
for await (const key of client.scanIterator()) {
// use the key!
await client.get(key);
}
```
This works with `HSCAN`, `SSCAN`, and `ZSCAN` too:
```typescript
for await (const { field, value } of client.hScanIterator("hash")) {
}
for await (const member of client.sScanIterator("set")) {
}
for await (const { score, value } of client.zScanIterator("sorted-set")) {
}
```
You can override the default options by providing a configuration object:
```typescript
client.scanIterator({
TYPE: "string", // `SCAN` only
MATCH: "patter*",
COUNT: 100,
});
```
### Disconnecting
The `QUIT` command has been deprecated in Redis 7.2 and should now also be considered deprecated in Node-Redis. Instead
of sending a `QUIT` command to the server, the client can simply close the network connection.
`client.QUIT/quit()` is replaced by `client.close()`. and, to avoid confusion, `client.disconnect()` has been renamed to
`client.destroy()`.
```typescript
client.destroy();
```
### Auto-Pipelining
Node Redis will automatically pipeline requests that are made during the same "tick".
```typescript
client.set("Tm9kZSBSZWRpcw==", "users:1");
client.sAdd("users:1:tokens", "Tm9kZSBSZWRpcw==");
```
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()`.
```typescript
await Promise.all([
client.set("Tm9kZSBSZWRpcw==", "users:1"),
client.sAdd("users:1:tokens", "Tm9kZSBSZWRpcw=="),
]);
```
### Programmability
See the [Programmability overview](./docs/programmability.md).
### Clustering
Check out the [Clustering Guide](./docs/clustering.md) when using Node Redis to connect to a Redis Cluster.
### Events
The Node Redis client class is an Nodejs EventEmitter and it emits an event each time the network status changes:
| Name | When | Listener arguments |
| ----------------------- | ---------------------------------------------------------------------------------- | --------------------------------------------------------- |
| `connect` | Initiating a connection to the server | _No arguments_ |
| `ready` | Client is ready to use | _No arguments_ |
| `end` | Connection has been closed (via `.disconnect()`) | _No arguments_ |
| `error` | An error has occurred—usually a network issue such as "Socket closed unexpectedly" | `(error: Error)` |
| `reconnecting` | Client is trying to reconnect to the server | _No arguments_ |
| `sharded-channel-moved` | See [here](./docs/pub-sub.md#sharded-channel-moved-event) | See [here](./docs/pub-sub.md#sharded-channel-moved-event) |
> :warning: You **MUST** listen to `error` events. If a client doesn't have at least one `error` listener registered and
> 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.
> The client will not emit [any other events](./docs/v3-to-v4.md#all-the-removed-events) beyond those listed above.
## Supported Redis versions
Node Redis is supported with the following versions of Redis:
| Version | Supported |
| ------- | ------------------ |
| 8.0.z | :heavy_check_mark: |
| 7.0.z | :heavy_check_mark: |
| 6.2.z | :heavy_check_mark: |
| 6.0.z | :heavy_check_mark: |
| 5.0.z | :heavy_check_mark: |
| < 5.0 | :x: |
> Node Redis should work with older versions of Redis, but it is not fully tested and we cannot offer support.
## Migration
- [From V3 to V4](docs/v3-to-v4.md)
- [From V4 to V5](docs/v4-to-v5.md)
- [V5](docs/v5.md)
## Contributing ## Contributing

View File

@@ -23,7 +23,7 @@ By default, each type is mapped to the first option in the lists below. To chang
- Double (`,`) => `number | string` - Double (`,`) => `number | string`
- Simple String (`+`) => `string | Buffer` - Simple String (`+`) => `string | Buffer`
- Blob String (`$`) => `string | Buffer` - Blob String (`$`) => `string | Buffer`
- Verbatim String (`=`) => `string | Buffer | VerbatimString` (TODO: `VerbatimString` typedoc link) - Verbatim String (`=`) => `string | Buffer | VerbatimString`
- Simple Error (`-`) => `ErrorReply` - Simple Error (`-`) => `ErrorReply`
- Blob Error (`!`) => `ErrorReply` - Blob Error (`!`) => `ErrorReply`
- Array (`*`) => `Array` - Array (`*`) => `Array`

View File

@@ -25,16 +25,22 @@ FUNCTION LOAD "#!lua name=library\nredis.register_function{function_name='add',
Load the prior redis function on the _redis server_ before running the example below. Load the prior redis function on the _redis server_ before running the example below.
```typescript ```typescript
import { createClient } from 'redis'; import { CommandParser } from '@redis/client/lib/client/parser';
import { NumberReply } from '@redis/client/lib/RESP/types';
import { createClient, RedisArgument } from 'redis';
const client = createClient({ const client = createClient({
functions: { functions: {
library: { library: {
add: { add: {
NUMBER_OF_KEYS: 1, NUMBER_OF_KEYS: 1,
FIRST_KEY_INDEX: 1, parseCommand(
transformArguments(key: string, toAdd: number): Array<string> { parser: CommandParser,
return [key, toAdd.toString()]; key: RedisArgument,
toAdd: RedisArgument
) {
parser.pushKey(key)
parser.push(toAdd)
}, },
transformReply: undefined as unknown as () => NumberReply transformReply: undefined as unknown as () => NumberReply
} }
@@ -43,9 +49,8 @@ const client = createClient({
}); });
await client.connect(); await client.connect();
await client.set('key', '1'); await client.set('key', '1');
await client.library.add('key', 2); // 3 await client.library.add('key', '2'); // 3
``` ```
## [Lua Scripts](https://redis.io/docs/manual/programmability/eval-intro/) ## [Lua Scripts](https://redis.io/docs/manual/programmability/eval-intro/)
@@ -53,7 +58,9 @@ await client.library.add('key', 2); // 3
The following is an end-to-end example of the prior concept. The following is an end-to-end example of the prior concept.
```typescript ```typescript
import { createClient, defineScript, NumberReply } from 'redis'; import { CommandParser } from '@redis/client/lib/client/parser';
import { NumberReply } from '@redis/client/lib/RESP/types';
import { createClient, defineScript, RedisArgument } from 'redis';
const client = createClient({ const client = createClient({
scripts: { scripts: {
@@ -61,8 +68,13 @@ const client = createClient({
SCRIPT: 'return redis.call("GET", KEYS[1]) + ARGV[1];', SCRIPT: 'return redis.call("GET", KEYS[1]) + ARGV[1];',
NUMBER_OF_KEYS: 1, NUMBER_OF_KEYS: 1,
FIRST_KEY_INDEX: 1, FIRST_KEY_INDEX: 1,
transformArguments(key: string, toAdd: number): Array<string> { parseCommand(
return [key, toAdd.toString()]; parser: CommandParser,
key: RedisArgument,
toAdd: RedisArgument
) {
parser.pushKey(key)
parser.push(toAdd)
}, },
transformReply: undefined as unknown as () => NumberReply transformReply: undefined as unknown as () => NumberReply
}) })
@@ -70,7 +82,6 @@ const client = createClient({
}); });
await client.connect(); await client.connect();
await client.set('key', '1'); await client.set('key', '1');
await client.add('key', 2); // 3 await client.add('key', '2'); // 3
``` ```

View File

@@ -234,12 +234,12 @@ In v5, any unwritten commands (in the same pipeline) will be discarded.
### Time Series ### Time Series
- `TS.ADD`: `boolean` -> `number` [^boolean-to-number] - `TS.ADD`: `boolean` -> `number` [^boolean-to-number]
- `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` - `TS.[M][REV]RANGE`: the `ALIGN` argument has been moved into `AGGREGATION`
- `TS.SYNUPDATE`: `Array<string | Array<string>>` -> `Record<string, Array<string>>` - `TS.SYNUPDATE`: `Array<string | Array<string>>` -> `Record<string, Array<string>>`
- `TS.M[REV]RANGE[_WITHLABELS]`, `TS.MGET[_WITHLABELS]`: TODO - `TimeSeriesDuplicatePolicies` -> `TIME_SERIES_DUPLICATE_POLICIES` [^enum-to-constants]
- `TimeSeriesEncoding` -> `TIME_SERIES_ENCODING` [^enum-to-constants]
[^enum-to-constants]: TODO - `TimeSeriesAggregationType` -> `TIME_SERIES_AGGREGATION_TYPE` [^enum-to-constants]
- `TimeSeriesReducers` -> `TIME_SERIES_REDUCERS` [^enum-to-constants]
- `TimeSeriesBucketTimestamp` -> `TIME_SERIES_BUCKET_TIMESTAMP` [^enum-to-constants]
[^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). [^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).
[^future-proofing]: TODO

View File

@@ -1,30 +1,83 @@
# RESP3 Support # RESP3 Support
TODO Node Redis v5 adds support for [RESP3](https://github.com/redis/redis-specifications/blob/master/protocol/RESP3.md), the new Redis serialization protocol. RESP3 offers richer data types and improved type handling compared to RESP2.
To use RESP3, specify it when creating your client:
```javascript ```javascript
import { createClient } from 'redis';
const client = createClient({ const client = createClient({
RESP: 3 RESP: 3
}); });
``` ```
## Type Mapping
With RESP3, you can leverage the protocol's richer type system. You can customize how different Redis types are represented in JavaScript using type mapping:
```javascript ```javascript
// by default import { createClient, RESP_TYPES } from 'redis';
// By default
await client.hGetAll('key'); // Record<string, string> await client.hGetAll('key'); // Record<string, string>
// Use Map instead of plain object
await client.withTypeMapping({ await client.withTypeMapping({
[TYPES.MAP]: Map [RESP_TYPES.MAP]: Map
}).hGetAll('key'); // Map<string, string> }).hGetAll('key'); // Map<string, string>
// Use both Map and Buffer
await client.withTypeMapping({ await client.withTypeMapping({
[TYPES.MAP]: Map, [RESP_TYPES.MAP]: Map,
[TYPES.BLOB_STRING]: Buffer [RESP_TYPES.BLOB_STRING]: Buffer
}).hGetAll('key'); // Map<string, Buffer> }).hGetAll('key'); // Map<string, Buffer>
``` ```
This replaces the previous approach of using `commandOptions({ returnBuffers: true })` in v4.
## PubSub in RESP3
RESP3 uses a different mechanism for handling Pub/Sub messages. Instead of modifying the `onReply` handler as in RESP2, RESP3 provides a dedicated `onPush` handler. When using RESP3, the client automatically uses this more efficient push notification system.
## Known Limitations
### Unstable Module Commands
Some Redis module commands have unstable RESP3 transformations. These commands will throw an error when used with RESP3 unless you explicitly opt in to using them by setting `unstableResp3: true` in your client configuration:
```javascript
const client = createClient({
RESP: 3,
unstableResp3: true
});
```
The following commands have unstable RESP3 implementations:
1. **Stream Commands**:
- `XREAD` and `XREADGROUP` - The response format differs between RESP2 and RESP3
2. **Search Commands (RediSearch)**:
- `FT.AGGREGATE`
- `FT.AGGREGATE_WITHCURSOR`
- `FT.CURSOR_READ`
- `FT.INFO`
- `FT.PROFILE_AGGREGATE`
- `FT.PROFILE_SEARCH`
- `FT.SEARCH`
- `FT.SEARCH_NOCONTENT`
- `FT.SPELLCHECK`
3. **Time Series Commands**:
- `TS.INFO`
- `TS.INFO_DEBUG`
If you need to use these commands with RESP3, be aware that the response format might change in future versions.
# Sentinel Support # Sentinel Support
[TODO](./sentinel.md) [Sentinel](./sentinel.md)
# `multi.exec<'typed'>` / `multi.execTyped` # `multi.exec<'typed'>` / `multi.execTyped`