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.
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.
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
- Your team has ioredis muscle memory or an existing ioredis codebase: the APIs are incompatible enough that migrating is real work for little payoff
- You deploy to serverless or edge runtimes with no TCP sockets (Cloudflare Workers, Vercel Edge): you need an HTTP-based client like @upstash/redis instead
- You are still on the v3/v4 API: each major has renamed core methods (quit/disconnect became close/destroy in v5, isolation pools became RedisClientPool), so upgrading is a migration project, not a version bump
- You forget error listeners: by the maintainers' own warning, a client without an 'error' listener will throw on any network hiccup and take the whole Node process down
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
| Package | Registry | Pick it when |
|---|---|---|
| ioredis | npm | The long-time community standard; huge installed base and a different but mature API, though new feature work happens in node-redis |
| @upstash/redis | npm | Serverless and edge runtimes where you need Redis over HTTP instead of a TCP connection |
| iovalkey | npm | You run Valkey rather than Redis and want a client maintained for that fork |