* 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>
12 KiB
v4 to v5 migration guide
Client Configuration
Keep Alive
To better align with Node.js build-in net and tls modules, the keepAlive option has been split into 2 options: keepAlive (boolean) and keepAliveInitialDelay (number). The defaults remain true and 5000.
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) => {
// ...
});
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.
Isolation Pool
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:
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:
const pool = await createClientPool()
.on('error', err => console.error(err))
.connect();
await pool.ping();
See the pool guide for more information.
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();
MULTI.execAsPipeline()
await client.multi()
.set('a', 'a')
.set('b', 'b')
.execAsPipeline();
In older versions, if the socket disconnects during the pipeline execution, i.e. after writing SET a a and before SET b b, the returned promise is rejected, but SET b b will still be executed on the server.
In v5, any unwritten commands (in the same pipeline) will be discarded.
RedisFlushModes->REDIS_FLUSH_MODES[^enum-to-constants]
Commands
Redis
-
ACL GETUSER:selectors -
COPY:destinationDb->DB,replace->REPLACE,boolean->number[^boolean-to-number] -
CLIENT KILL:enum ClientKillFilters->const CLIENT_KILL_FILTERS[^enum-to-constants] -
CLUSTER FAILOVER:enum FailoverModes->const FAILOVER_MODES[^enum-to-constants] -
CLIENT TRACKINGINFO:flagsin RESP2 -Set<string>->Array<string>(to match RESP3 default type mapping) -
CLUSTER INFO: -
CLUSTER SETSLOT:ClusterSlotStates->CLUSTER_SLOT_STATES[^enum-to-constants] -
CLUSTER RESET: the second argument is{ mode: string; }instead ofstring[^future-proofing] -
CLUSTER FAILOVER:enum FailoverModes->const FAILOVER_MODES[^enum-to-constants], the second argument is{ mode: string; }instead ofstring[^future-proofing] -
CLUSTER LINKS:createTime->create-time,sendBufferAllocated->send-buffer-allocated,sendBufferUsed->send-buffer-used1 -
CLUSTER NODES,CLUSTER REPLICAS,CLUSTER INFO: returning the rawVerbatimStringReply -
EXPIRE:boolean->number[^boolean-to-number] -
EXPIREAT:boolean->number[^boolean-to-number] -
HSCAN:tupleshas been renamed toentries -
HEXISTS:boolean->number[^boolean-to-number] -
HRANDFIELD_COUNT_WITHVALUES:Record<BlobString, BlobString>->Array<{ field: BlobString; value: BlobString; }>(it can return duplicates). -
HSETNX:boolean->number[^boolean-to-number] -
INFO: -
LCS IDX:lengthhas been changed tolen,matcheshas been changed fromArray<{ key1: RangeReply; key2: RangeReply; }>toArray<[key1: RangeReply, key2: RangeReply]> -
ZINTER: 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 }]) -
ZMPOP:{ elements: Array<{ member: string; score: number; }>; }->{ members: Array<{ value: string; score: number; }>; }to match other sorted set commands (e.g.ZRANGE,ZSCAN) -
MOVE:boolean->number[^boolean-to-number] -
PEXPIRE:boolean->number[^boolean-to-number] -
PEXPIREAT:boolean->number[^boolean-to-number] -
PFADD:boolean->number[^boolean-to-number] -
RENAMENX:boolean->number[^boolean-to-number] -
SETNX:boolean->number[^boolean-to-number] -
SCAN,HSCAN,SSCAN, andZSCAN:reply.cursorwill not be converted to number to avoid issues when the number is bigger thanNumber.MAX_SAFE_INTEGER. See here. -
SCRIPT EXISTS:Array<boolean>->Array<number>[^boolean-to-number] -
SISMEMBER:boolean->number[^boolean-to-number] -
SMISMEMBER:Array<boolean>->Array<number>[^boolean-to-number] -
SMOVE:boolean->number[^boolean-to-number] -
GEOSEARCH_WITH/GEORADIUS_WITH:GeoReplyWith->GEO_REPLY_WITH[^enum-to-constants] -
GEORADIUSSTORE->GEORADIUS_STORE -
GEORADIUSBYMEMBERSTORE->GEORADIUSBYMEMBER_STORE -
XACK:boolean->number[^boolean-to-number] -
XADD: theINCRoption has been removed, useXADD_INCRinstead -
LASTSAVE:Date->number(unix timestamp) -
HELLO:protovermoved from the options object to it's own argument,auth->AUTH,clientName->SETNAME -
MODULE LIST:version->ver1 -
MEMORY STATS: 1 -
FUNCTION RESTORE: the second argument is{ mode: string; }instead ofstring[^future-proofing] -
FUNCTION STATS:runningScript->running_script,durationMs->duration_ms,librariesCount->libraries_count,functionsCount->functions_count1 -
TIME:Date->[unixTimestamp: string, microseconds: string] -
XGROUP_CREATECONSUMER: [^boolean-to-number] -
XGROUP_DESTROY: [^boolean-to-number] -
XINFO GROUPS:lastDeliveredId->last-delivered-id1 -
XINFO STREAM:radixTreeKeys->radix-tree-keys,radixTreeNodes->radix-tree-nodes,lastGeneratedId->last-generated-id,maxDeletedEntryId->max-deleted-entry-id,entriesAdded->entries-added,recordedFirstEntryId->recorded-first-entry-id,firstEntry->first-entry,lastEntry->last-entry -
XAUTOCLAIM,XCLAIM,XRANGE,XREVRANGE:Array<{ name: string; messages: Array<{ id: string; message: Record<string, string> }>; }>->Record<string, Array<{ id: string; message: Record<string, string> }>> -
COMMAND LIST:enum FilterBy->const COMMAND_LIST_FILTER_BY[^enum-to-constants], the filter argument has been moved from a "top level argument" into{ FILTERBY: { type: <MODULE|ACLCAT|PATTERN>; value: <value> } }
Bloom
TOPK.QUERY:Array<number>->Array<boolean>
JSON
JSON.ARRINDEX:startandendarguments moved to{ range: { start: number; end: number; }; }[^future-proofing]JSON.ARRPOP:pathandindexarguments moved to{ path: string; index: number; }[^future-proofing]JSON.ARRLEN,JSON.CLEAR,JSON.DEBUG MEMORY,JSON.DEL,JSON.FORGET,JSON.OBJKEYS,JSON.OBJLEN,JSON.STRAPPEND,JSON.STRLEN,JSON.TYPE:pathargument moved to{ path: string; }[^future-proofing]
Search
FT.SUGDEL: [^boolean-to-number]FT.CURSOR READ:cursortype changed fromnumbertostring(in and out) to avoid issues when the number is bigger thanNumber.MAX_SAFE_INTEGER. See here.AggregateGroupByReducers->FT_AGGREGATE_GROUP_BY_REDUCERS[^enum-to-constants]AggregateSteps->FT_AGGREGATE_STEPS[^enum-to-constants]RedisSearchLanguages->REDISEARCH_LANGUAGE[^enum-to-constants]SchemaFieldTypes->SCHEMA_FIELD_TYPE[^enum-to-constants]SchemaTextFieldPhonetics->SCHEMA_TEXT_FIELD_PHONETIC[^enum-to-constants]SearchOptions->FtSearchOptionsVectorAlgorithms->SCHEMA_VECTOR_FIELD_ALGORITHM[^enum-to-constants]
Time Series
TS.ADD:boolean->number[^boolean-to-number]TS.[M][REV]RANGE: theALIGNargument has been moved intoAGGREGATIONTS.SYNUPDATE:Array<string | Array<string>>->Record<string, Array<string>>TimeSeriesDuplicatePolicies->TIME_SERIES_DUPLICATE_POLICIES[^enum-to-constants]TimeSeriesEncoding->TIME_SERIES_ENCODING[^enum-to-constants]TimeSeriesAggregationType->TIME_SERIES_AGGREGATION_TYPE[^enum-to-constants]TimeSeriesReducers->TIME_SERIES_REDUCERS[^enum-to-constants]TimeSeriesBucketTimestamp->TIME_SERIES_BUCKET_TIMESTAMP[^enum-to-constants]