7.6 KiB
v4 to v5 migration guide
Command Options
In v4, command options are passed as a first optional argument:
await client.get('key'); // `string | null`
await client.get(client.commandOptions({ returnBuffers: true }), 'key'); // `Buffer | null`
This has a couple of flaws:
- The argument types are checked in runtime, which is a performance hit.
- Code suggestions are less readable/usable, due to "function overloading".
- Overall, "user code" is not as readable as it could be.
The new API for v5
With the new API, instead of passing the options directly to the commands we use a "proxy client" to store them:
await client.get('key'); // `string | null`
const proxyClient = client.withCommandOptions({
typeMapping: {
[TYPES.BLOB_STRING]: Buffer
}
});
await proxyClient.get('key'); // `Buffer | null`
for more information, see the Command Options guide.
Quit VS Disconnect
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()
.
Scan Iterators
Iterator commands like SCAN
, HSCAN
, SSCAN
, and ZSCAN
return collections of elements (depending on the data type). However, v4 iterators loop over these collections and yield individual items:
for await (const key of client.scanIterator()) {
console.log(key, await client.get(key));
}
This mismatch can be awkward and makes "multi-key" commands like MGET
, UNLINK
, etc. pointless. So, in v5 the iterators now yield a collection instead of an element:
for await (const keys of client.scanIterator()) {
// we can now meaningfully utilize "multi-key" commands
console.log(keys, await client.mGet(keys));
}
for more information, see the Scan Iterators guide.
Legacy Mode
In the previous version, you could access "legacy" mode by creating a client and passing in { legacyMode: true }
. Now, you can create one off of an existing client by calling the .legacy()
function. This allows easier access to both APIs and enables better TypeScript support.
// use `client` for the current API
const client = createClient();
await client.set('key', 'value');
// use `legacyClient` for the "legacy" API
const legacyClient = client.legacy();
legacyClient.set('key', 'value', (err, reply) => {
// ...
});
Isolation Pool
TODO
await client.sendCommand(['GET', 'key']);
const pool = client.createPool({
min: 0,
max: Infinity
});
await pool.blPop('key');
await pool.sendCommand(['GET', 'key']);
await pool.use(client => client.blPop());
await cluster.sendCommand('key', true, ['GET', 'key']);
const clusterPool = cluster.createPool({
min: 0,
max: Infinity
});
await clusterPool.blPop('key');
await clusterPool.sendCommand('key', true, ['GET', 'key']);
await clusterPool.use(client => client.blPop());
Cluster MULTI
In v4, cluster.multi()
did not support executing commands on replicas, even if they were readonly.
// this might execute on a replica, depending on configuration
await cluster.sendCommand('key', true, ['GET', 'key']);
// this always executes on a master
await cluster.multi()
.addCommand('key', ['GET', 'key'])
.exec();
To support executing commands on replicas, cluster.multi().addCommand
now requires isReadonly
as the second argument, which matches the signature of cluster.sendCommand
:
await cluster.multi()
.addCommand('key', true, ['GET', 'key'])
.exec();
Commands
Some command arguments/replies have changed to align more closely to data types returned by Redis:
ACL GETUSER
:selectors
CLIENT KILL
:enum ClientKillFilters
->const CLIENT_KILL_FILTERS
1CLUSTER FAILOVER
:enum FailoverModes
->const FAILOVER_MODES
1LCS IDX
:length
has been changed tolen
,matches
has been changed fromArray<{ key1: RangeReply; key2: RangeReply; }>
toArray<[key1: RangeReply, key2: RangeReply]>
HEXISTS
:boolean
->number
2HRANDFIELD_COUNT_WITHVALUES
:Record<BlobString, BlobString>
->Array<{ field: BlobString; value: BlobString; }>
(it can return duplicates).SCAN
,HSCAN
,SSCAN
, andZSCAN
: cursor type isstring
instead ofnumber
?HSETNX
:boolean
->number
2ZINTER
: instead ofclient.ZINTER('key', { WEIGHTS: [1] })
useclient.ZINTER({ key: 'key', weight: 1 }])
ZINTER_WITHSCORES
: instead ofclient.ZINTER_WITHSCORES('key', { WEIGHTS: [1] })
useclient.ZINTER_WITHSCORES({ key: 'key', weight: 1 }])
ZUNION
: instead ofclient.ZUNION('key', { WEIGHTS: [1] })
useclient.ZUNION({ key: 'key', weight: 1 }])
ZUNION_WITHSCORES
: instead ofclient.ZUNION_WITHSCORES('key', { WEIGHTS: [1] })
useclient.ZUNION_WITHSCORES({ key: 'key', weight: 1 }])
SETNX
:boolean
->number
2COPY
:destinationDb
->DB
,replace
->REPLACE
,boolean
->number
2EXPIRE
:boolean
->number
2EXPIREAT
:boolean
->number
2MOVE
:boolean
->number
2PEXPIRE
:boolean
->number
2PEXPIREAT
:boolean
->number
2RENAMENX
:boolean
->number
2HSCAN
:tuples
has been renamed toentries
PFADD
:boolean
->number
2SCRIPT EXISTS
:Array<boolean>
->Array<number>
2SISMEMBER
:boolean
->number
2SMISMEMBER
:Array<boolean>
->Array<number>
2SMOVE
:boolean
->number
2TS.ADD
:boolean
->number
2GEOSEARCH_WITH
/GEORADIUS_WITH
:GeoReplyWith
->GEO_REPLY_WITH
1GEORADIUSSTORE
->GEORADIUS_STORE
GEORADIUSBYMEMBERSTORE
->GEORADIUSBYMEMBER_STORE
XACK
:boolean
->number
2XADD
: theINCR
option has been removed, useXADD_INCR
insteadLASTSAVE
:Date
->number
(unix timestamp)HELLO
:protover
moved from the options object to it's own argument,auth
->AUTH
,clientName
->SETNAME
MODULE LIST
:version
->ver
3MEMORY STATS
: 3CLIENT TRACKINGINFO
:flags
in RESP2 -Set<string>
->Array<string>
(to match RESP3 default type mapping)CLUSETER SETSLOT
:ClusterSlotStates
->CLUSTER_SLOT_STATES
1FUNCTION RESTORE
: the second argument is{ mode: string; }
instead ofstring
4CLUSTER RESET
: the second argument is{ mode: string; }
instead ofstring
4CLUSTER FAILOVER
:enum FailoverModes
->const FAILOVER_MODES
1 , the second argument is{ mode: string; }
instead ofstring
4CLUSTER LINKS
:createTime
->create-time
,sendBufferAllocated
->send-buffer-allocated
,sendBufferUsed
->send-buffer-used
3TIME
:Date
->[unixTimestamp: string, microseconds: string]
ZMPOP
:{ elements: Array<{ member: string; score: number; }>; }
->{ members: Array<{ value: string; score: number; }>; }
to match other sorted set commands (e.g.ZRANGE
,ZSCAN
)