Connecting to a cluster is a bit different. Create the client by specifying some (or all) of the nodes in your cluster and then use it like a regular client instance:
import { createCluster } from 'redis';
const cluster = await createCluster({
rootNodes: [{
url: 'redis://10.0.0.1:30001'
}, {
url: 'redis://10.0.0.2:30002'
}]
})
.on('error', err => console.log('Redis Cluster Error', err))
.connect();
await cluster.set('key', 'value');
const value = await cluster.get('key');
await cluster.close();See the client configuration page for the
rootNodesanddefaultsconfiguration schemas.
| Property | Default | Description |
|---|---|---|
| rootNodes | An array of root nodes that are part of the cluster, which will be used to get the cluster topology. Each element in the array is a client configuration object. There is no need to specify every node in the cluster: 3 should be enough to reliably connect and obtain the cluster configuration from the server | |
| defaults | The default configuration values for every client in the cluster. Use this for example when specifying an ACL user to connect with | |
| useReplicas | false |
When true, distribute load by executing readonly commands (such as GET, GEOSEARCH, etc.) across all cluster nodes. When false, only use master nodes |
| minimizeConnections | false |
When true, .connect() will only discover the cluster topology, without actually connecting to all the nodes. Useful for short-term or Pub/Sub-only connections. |
| maxCommandRedirections | 16 |
The maximum number of times a command will be redirected due to MOVED or ASK errors |
| nodeAddressMap | Defines the node address mapping | |
| keyPrefix | Prefix prepended to every key sent to Redis (ioredis-compatible). See Key Prefixing. | |
| commandOptions | Default Command Options (e.g. timeout, typeMapping) for commands executed on the cluster |
|
| clientSideCache | Client Side Caching configuration for the cluster (RESP3 only; shared across cluster nodes). See V5 docs | |
| topologyRefreshOnReconnectionAttemptStrategy | 5000 |
Delay in ms after a node reconnection attempt before background cluster topology refreshes are triggered. Set to false or 0 to disable, or provide a dynamic strategy function |
| modules | Included Redis Modules | |
| scripts | Script definitions (see Lua Scripts) | |
| functions | Function definitions (see Functions) |
Most redis commands are the same as with individual clients.
If you want to run commands and/or use arguments that Node Redis doesn't know about (yet!) use .sendCommand().
When clustering, sendCommand takes 3 arguments to help with routing to the correct redis node:
firstKey: the key that is being operated on, orundefinedto route to a random node.isReadOnly: determines if the command needs to go to the master or may go to a replica.args: the command and all arguments (including the key), as an array of strings.
await cluster.sendCommand("key", false, ["SET", "key", "value", "NX"]); // 'OK'
await cluster.sendCommand("key", true, ["HGETALL", "key"]); // ['key1', 'field1', 'key2', 'field2']The configuration in rootNodes is only used for the connections that discover the cluster topology. It is not inherited by the connections the cluster then makes to the discovered nodes — those are created from the defaults option (plus the discovered host and port).
This means that any setting that should apply to every connection in the cluster — credentials, TLS, timeouts, etc. — must be specified via defaults, even if it is already present in a root node URL or configuration.
Specifying the password in the URL or a root node will only affect the connection used for topology discovery. In case you want to set the password for all the connections being created from a cluster instance, use the defaults option.
createCluster({
rootNodes: [{
url: 'redis://10.0.0.1:30001'
}, {
url: 'redis://10.0.0.2:30002'
}],
defaults: {
username: 'username',
password: 'password'
}
});Likewise, TLS options specified in the URL (rediss://) or in a root node only affect the topology discovery connection. If your cluster requires TLS (e.g. AWS ElastiCache with in-transit encryption enabled), enable it via defaults.socket, otherwise the connections to the discovered nodes will be attempted in plaintext and can hang without an error:
createCluster({
rootNodes: [{
url: 'rediss://external-host.io:30001'
}],
defaults: {
socket: {
tls: true
}
}
});A mapping between the addresses in the cluster (see CLUSTER SHARDS) and the addresses the client should connect to.
Useful when the cluster is running on a different network to the client.
const rootNodes = [{
url: 'external-host-1.io:30001'
}, {
url: 'external-host-2.io:30002'
}];
// Use either a static mapping:
createCluster({
rootNodes,
nodeAddressMap: {
'10.0.0.1:30001': {
host: 'external-host.io',
port: 30001
},
'10.0.0.2:30002': {
host: 'external-host.io',
port: 30002
}
}
});
// or create the mapping dynamically, as a function:
createCluster({
rootNodes,
nodeAddressMap(address) {
const indexOfDash = address.lastIndexOf('-'),
indexOfDot = address.indexOf('.', indexOfDash),
indexOfColons = address.indexOf(':', indexOfDot);
return {
host: `external-host-${address.substring(indexOfDash + 1, indexOfDot)}.io`,
port: Number(address.substring(indexOfColons + 1))
};
}
});This is a common problem when using ElastiCache. See Accessing ElastiCache from outside AWS for more information on that.
The Node Redis Cluster class extends Node.js’s EventEmitter and emits the following events:
| Name | When | Listener arguments |
|---|---|---|
connect |
The cluster has successfully connected and is ready to us | No arguments |
disconnect |
The cluster has disconnected | No arguments |
error |
The cluster has errored | (error: Error) |
node-ready |
A cluster node is ready to use, on its first connection and on every reconnection | (node: { host: string, port: number }) |
node-connect |
A cluster node's socket has connected, before its handshake — on the first connection and on every reconnection | (node: { host: string, port: number }) |
node-reconnecting |
A cluster node is attempting to reconnect after an error | (node: { host: string, port: number }) |
node-disconnect |
A cluster node has disconnected (via close() or destroy()) |
(node: { host: string, port: number }) |
node-error |
A cluster node has has errored (usually during TCP connection) | (error: Error, node: { host: string, port: number }) |
⚠️ You MUST listen toerrorevents. If a cluster doesn't have at least oneerrorlistener registered and anerroroccurs, that error will be thrown and the Node.js process will exit. See the >EventEmitterdocs for more details.
Commands such as GET, SET, etc. are routed by the first key specified. For example MGET 1 2 3 will be routed by the key 1.
Admin commands such as MEMORY STATS, FLUSHALL, etc. are not attached to the cluster, and must be executed on a specific node via .getSlotMaster().
Certain commands (e.g. PUBLISH) are forwarded to other cluster nodes by the Redis server. The client sends these commands to a random node in order to spread the load across the cluster.
WATCH relies on connection-level state on a specific node, so it isn't exposed directly on the cluster client. Use .getNodeClientForKey() to get the node client responsible for a key's slot and run the optimistic-locking transaction on it:
const key = 'key';
const nodeClient = await cluster.getNodeClientForKey(key);
await nodeClient.watch(key);
const value = await nodeClient.get(key);
const reply = await nodeClient
.multi()
.set(key, calculateNewValue(value)) // application logic
.exec(); // `null` if `key` changed since `WATCH`, retry in that caseAll keys touched in the transaction must hash to the same slot. Pass true as the second argument (getNodeClientForKey(key, true)) to allow a replica for read-only use.