mrkeyoor.com_
Wed 05 Aug 05:04 UTC
npmDataupdated 05 Aug 2026

redis

node-redis is the official Redis client for Node.js, maintained by Redis Inc. It gives you every Redis command as a promise-returning method (both HGETALL and hGetAll spellings), plus transactions, pub/sub, scan iterators, clustering, sentinel, connection pooling, and client-side caching. The npm package 'redis' is the all-in-one build that bundles the JSON, search, bloom, and time-series module commands on top of the core client.

Verdict

The right default for new Node projects talking to Redis, especially with Stack modules or Redis Cloud. Budget real time for the v5+ API differences from every tutorial written before 2024, and never ship without an error listener.

API stability3/5v3 to v4 was a full rewrite and v5 renamed core lifecycle methods again (quit to close, disconnect to destroy) and extracted pooling into RedisClientPool; the current API is good but history says majors break you.
Docs4/5The README covers the essentials well and dedicated guides exist for pub/sub, clustering, sentinel, pooling, and v4-to-v5 migration; the catch is that most third-party tutorials still show the old API, so you must read the repo docs, not blogs.
Maintenance5/5Maintained by Redis Inc with pushes as recent as this week, a 6.2.0 release days old, and fast support for new server features like Redis 8.4 CAS/CAD commands.
Ecosystem4/512.6M weekly downloads, official module packages for JSON/search/bloom/time-series, OpenTelemetry and diagnostics_channel integration; ioredis still owns a large share of existing deployments and third-party integrations.

Use it if

  • You want the official client that tracks new Redis server features first (client-side caching with RESP3, the 8.4 compare-and-set commands, OpenTelemetry metrics)
  • You need module commands (RedisJSON, RediSearch, time series, bloom filters) with typed APIs instead of hand-built sendCommand calls
  • You are on Redis Cloud or a Redis Stack deployment where matching the vendor's own client removes a class of compatibility questions
  • You like async iterators for SCAN and promise-native transactions instead of callback-era patterns
Skip it if

Setup reality

npm install redis pulls in five @redis/* packages; there is no native compilation and setup is genuinely quick. The real friction is operational: you must attach an error listener before connect or a dropped socket kills your process, connect() is explicit and async, reconnection strategy is your problem to configure, and the v4 to v5/v6 renames (disconnect to destroy, quit to close, pool extraction) mean most blog posts and older Stack Overflow answers show an API that no longer exists. The optional local digest helper additionally wants a @node-rs/xxhash native peer dependency.

Patterns

Create and connect a clientconnect-client

import { createClient } from 'redis';

const client = await createClient({ url: 'redis://localhost:6379' })
  .on('error', (err) => console.error('Redis error', err))
  .connect();

await client.set('key', 'value');
console.log(await client.get('key'));

The error listener is mandatory: without one, any socket error is thrown and exits the Node process.

Set a key with expiry and NXset-with-ttl

await client.set('session:abc', token, {
  EX: 3600, // seconds
  NX: true, // only if it does not exist
});

Command modifiers are an options object, not extra string arguments like in redis-cli examples.

Store and read a hashhash-operations

await client.hSet('user:1', { name: 'Ada', role: 'admin' });
const user = await client.hGetAll('user:1');
// { name: 'Ada', role: 'admin' }

hGetAll on a missing key returns an empty object, not null; check Object.keys(user).length.

Run a MULTI/EXEC transactiontransaction-multi

const [setReply, count] = await client
  .multi()
  .set('key', 'value')
  .incr('counter')
  .exec();

Commands queue locally and run atomically on exec(); combine with client.watch() for optimistic locking.

Iterate keys without blocking Redisscan-keys

for await (const keys of client.scanIterator({ MATCH: 'user:*', COUNT: 100 })) {
  const values = await client.mGet(keys);
}

The iterator yields batches of keys, not single keys; never use KEYS in production, SCAN exists for this.

Publish and subscribepub-sub

const subscriber = client.duplicate();
subscriber.on('error', console.error);
await subscriber.connect();

await subscriber.subscribe('news', (message) => {
  console.log(message);
});

await client.publish('news', 'hello');

A subscribed connection cannot run other commands, so you must duplicate() a dedicated client for subscriptions.

Use a connection poolconnection-pool

import { createClientPool } from 'redis';

const pool = await createClientPool({ url: 'redis://localhost:6379' })
  .on('error', console.error)
  .connect();

await pool.ping();

v5 replaced the v4 'isolation pool' option with this class; use a pool when you need blocking commands like BLPOP without stalling other traffic.

Send a command the client does not knowraw-command

await client.sendCommand(['SET', 'key', 'value', 'NX']); // 'OK'
const raw = await client.sendCommand(['HGETALL', 'key']);

sendCommand returns raw flat replies (HGETALL gives an array, not an object) and the API differs on clusters.

Enable client-side cachingclient-side-caching

const client = createClient({
  RESP: 3,
  clientSideCache: {
    ttl: 0,
    maxEntries: 0,
    evictPolicy: 'LRU',
  },
});

Requires RESP3; the server invalidates the local cache for you, which cuts round trips on hot read keys.

Batch commands in one round tripauto-pipelining

await Promise.all([
  client.set('a', '1'),
  client.sAdd('tags', 'x'),
  client.incr('counter'),
]);

Commands issued in the same tick pipeline automatically; use Promise.all so no rejection goes unhandled.

Close the connection cleanlygraceful-shutdown

// wait for pending replies, then close
await client.close();

// or tear down immediately
client.destroy();

quit() and disconnect() are the deprecated v4 names; close() replaces quit and destroy() replaces disconnect.

Work with Buffers instead of stringsbinary-values

import { RESP_TYPES } from 'redis';

const binary = client.withTypeMapping({
  [RESP_TYPES.BLOB_STRING]: Buffer,
});

const dump = await binary.dump('source');
await binary.restore('destination', 0, dump);

DUMP/RESTORE payloads corrupt if decoded as UTF-8 strings; map blob strings to Buffer first.

Alternatives

PackageRegistryPick it when
ioredisnpmThe long-time community standard; huge installed base and a different but mature API, though new feature work happens in node-redis
@upstash/redisnpmServerless and edge runtimes where you need Redis over HTTP instead of a TCP connection
iovalkeynpmYou run Valkey rather than Redis and want a client maintained for that fork