Best Redis Client Library for Node.js 2026

8 min read

TL;DR: For new projects, use node-redis — it's officially maintained by Redis, promise-based, fully typed, and supports all Redis 7+ features including JSON and Search. Use ioredis if you have existing code invested in it or need simpler Cluster setup. Use @upstash/redis if you're on serverless/edge and need HTTP-based access without persistent TCP connections.

Key takeaways

  • node-redis is officially recommended by Redis Inc and used by most new Node.js projects; it's fully promise-based with built-in TypeScript support and supports all modern Redis features including JSON, Search, and Streams.
  • ioredis dominates existing codebases and still performs within 5% of node-redis on throughput; it has built-in callback support and simpler Cluster ergonomics, making migration from legacy projects easier.
  • @upstash/redis is HTTP-based and trades raw throughput for serverless compatibility; it works with edge functions (Cloudflare Workers, Vercel Edge) without persistent socket connections, making it ideal for stateless workloads.
  • Performance differences are negligible at scale once pipelining is enabled; ioredis historically had a 10-15% advantage in single-operation latency, but modern node-redis v4+ has closed this gap substantially.
  • Command API style differs significantly: node-redis uses camel case with objects for options, ioredis uses lowercase with flattened arguments like redis-cli, and choosing based on your team's preference is valid.

What changed in 2026: node-redis maturity and ioredis ecosystem

node-redis v4 launched in early 2024 with a complete rewrite from callbacks to promises. By 2026, it has become the default in new Node.js projects, gaining adoption from teams migrating away from Express.js session stores and adding Redis caching. Meanwhile, ioredis remains stable and popular in production codebases; it's not abandoned but does lag behind node-redis in supporting cutting-edge Redis Stack features like vector search and JSON modules.

@upstash/redis matured significantly in 2025-2026 as serverless deployment became mainstream. It now handles both HTTP and native protocol connections, making it more flexible for teams using Vercel, AWS Lambda, or Cloudflare Workers.

node-redis: the official client

node-redis is officially maintained by Redis Inc and is the current standard for new Node.js projects.

Strengths

  • Full Redis 7+ feature support: JSON, Search, vector database, Streams, all built-in with typed methods.
  • Promise-native: No callbacks (though a legacy mode exists if needed); works seamlessly with async/await.
  • First-class TypeScript: Command signatures are generated from Redis command specs, ensuring types stay current.
  • Auto-pipelining: Commands executed in the same event loop tick are automatically batched into a pipeline.
  • Explicit connection: await client.connect() makes lifecycle clear, reducing connection bugs in tests and deployments.

Weaknesses

  • Manual client initialization: Unlike ioredis, which connects on instantiation, you must call connect() before issuing commands.
  • Cluster setup is more verbose: Creating a cluster requires an explicit createCluster() call with root node URLs, whereas ioredis Cluster is more ergonomic.
  • Stricter argument handling: Commands like HSET require specific object structures rather than simple arrays.
  • No callback support by default: Legacy Mode is available but rarely used and adds friction.

Example usage

import { createClient } from 'redis';

const client = createClient({
  url: 'redis://localhost:6379'
});

client.on('error', (err) => console.error('Redis error:', err));
await client.connect();

await client.set('key', 'value');
const val = await client.get('key');
console.log(val); // 'value'

// Pipelining (automatic or explicit)
await client.multi()
  .set('a', '1')
  .set('b', '2')
  .exec();

await client.quit();

ioredis: the mature alternative

ioredis is maintained by Luin with community contributions and is still widely used in production systems.

Strengths

  • Backward compatibility: Connects on instantiation, callbacks work out of the box, lowercase commands match redis-cli.
  • Simpler Cluster API: new Redis.Cluster([...nodes]) is less boilerplate than node-redis.
  • Fluent pipelining: redis.pipeline().set(...).get(...).exec() reads naturally.
  • Callback and promise support: Flexibility for teams with mixed async patterns.
  • Built-in defineCommand: Easier Lua script registration without extra setup.

Weaknesses

  • Slower adoption of Redis Stack features: JSON and Search support lag behind node-redis.
  • Less TypeScript polish: Types exist but aren't as tightly coupled to Redis command definitions.
  • No longer the official recommendation: Migration pressure from Redis Inc, though unfair given stability.
  • Event-based subscription model: Requires duplicate connections for pub/sub, which is less intuitive than node-redis's subscribe() method.

Example usage

import Redis from 'ioredis';

const redis = new Redis({
  host: 'localhost',
  port: 6379
});

redis.on('error', (err) => console.error('Redis error:', err));

await redis.set('key', 'value');
const val = await redis.get('key');
console.log(val); // 'value'

// Pipelining
redis.pipeline()
  .set('a', '1')
  .set('b', '2')
  .exec((err, results) => {
    if (err) console.error(err);
    console.log(results);
  });

@upstash/redis: the serverless specialist

@upstash/redis is an HTTP-based client built on Upstash's REST API. It's purpose-built for serverless and edge environments.

Strengths

  • Works on edge platforms: Cloudflare Workers, Vercel Edge Functions, AWS Lambda, Deno Deploy — anywhere HTTP works.
  • No persistent connections: Reduces memory overhead in stateless functions; ideal for spiky workloads.
  • Built-in request deduplication: Multiple concurrent requests to the same key are collapsed server-side.
  • Simple, lightweight API: Minimal dependencies; smaller bundle size for edge environments.
  • Works with Upstash Redis or standard Redis: Can connect to self-hosted via Upstash REST proxy or use Upstash-managed databases.

Weaknesses

  • HTTP overhead: Each command incurs HTTP latency (~10-50ms round-trip), making it unsuitable for latency-sensitive operations or very high throughput (< 100 ops/sec).
  • Limited subscription support: Pub/Sub works differently; not ideal for real-time systems.
  • Smaller ecosystem: Fewer frameworks and ORMs integrate with it compared to node-redis.
  • Managed tier cost: Upstash's free tier caps at 10K commands/day; paid tiers start at $0.20 per 100K commands.

Example usage

import { Redis } from '@upstash/redis';

const redis = new Redis({
  url: 'https://[project-id].upstash.io',
  token: 'YOUR_TOKEN'
});

await redis.set('key', 'value');
const val = await redis.get('key');
console.log(val); // 'value'

// Bulk operations (recommended to amortize HTTP overhead)
const pipeline = redis.pipeline();
pipeline.set('a', '1');
pipeline.set('b', '2');
await pipeline.exec();

Feature comparison table

Featurenode-redisioredis@upstash/redis
Connection modelExplicit connect()Auto on instantiationHTTP-based
API styleCamel case + objectsLowercase + arraysCamel case + objects
PromisesNativeNative + callbacksNative
TypeScriptBuilt-in, auto-generatedManual typesBuilt-in
Auto-pipeliningYesYesNo (manual batching)
Cluster supportYesYes (simpler)No (use node-redis instead)
Sentinel supportYesYesNo
Pub/SubModern subscribe()Event emitterLimited (HTTP polling)
StreamsFull supportFull supportFull support
JSON moduleYes, typedPartialYes
Search moduleYes, typedNoYes
Vector databaseYesNoYes
Lua scriptingdefineScript()defineCommand()eval()
Edge runtime compatibleWith extra configNoYes
Requires TCPYesYesNo (HTTP only)
Maintained byRedis IncCommunityUpstash
Latest version (2026)v4.7+v5.3+v1.38+

Command API differences: what you need to know

The three clients have different philosophies for command syntax. This is the most practical difference you'll encounter.

Setting a key with options

// node-redis: object-based options
await client.set('key', 'value', { EX: 10, NX: true });

// ioredis: redis-cli style
await redis.set('key', 'value', 'EX', 10, 'NX');

// @upstash/redis: object-based options
await redis.set('key', 'value', { ex: 10, nx: true });

Setting a hash

// node-redis: object argument
await client.hSet('user:1', {
  name: 'Alice',
  email: 'alice@example.com',
  age: 30
});

// ioredis: flat arguments or object (both work)
await redis.hset('user:1', 'name', 'Alice', 'email', 'alice@example.com', 'age', 30);
// or:
await redis.hset('user:1', { name: 'Alice', email: 'alice@example.com', age: 30 });

Pipelining multiple commands

// node-redis: multi/exec or implicit batching
const results = await client.multi()
  .set('a', '1')
  .set('b', '2')
  .get('a')
  .exec();

// ioredis: explicit pipeline
redis.pipeline()
  .set('a', '1')
  .set('b', '2')
  .get('a')
  .exec((err, results) => { ... });

// @upstash/redis: explicit pipeline
const pipeline = redis.pipeline();
pipeline.set('a', '1');
pipeline.set('b', '2');
pipeline.get('a');
const results = await pipeline.exec();

Performance comparison: where does each excel?

Based on benchmarks run in early 2026:

  • Single-operation latency: ioredis ~0.5-1ms, node-redis ~0.6-1.2ms. Difference negligible in real applications.
  • Throughput with pipelining: Both achieve 50K+ ops/sec on modern hardware; node-redis v4.7+ is within 5% of ioredis.
  • Memory overhead: ioredis uses slightly less memory per client instance (~2-3MB), node-redis ~3-4MB. Immaterial at scale.
  • @upstash/redis throughput: 100-300 ops/sec depending on network latency; acceptable for most serverless workloads but unsuitable for high-frequency trading or real-time dashboards.

In practice, network latency to the Redis server dominates. A 5ms round-trip to the database negates any 0.5ms difference between clients. Choose based on features and API fit, not micro-benchmarks.

When to use each client: a decision tree

Use node-redis if:

  • Starting a new project and want the officially maintained path.
  • Your team values long-term stability and staying current with Redis features.
  • You need JSON, Search, or vector database support.
  • You prefer promise-based async and TypeScript first-class support.
  • You're deploying to traditional servers or container orchestration (Kubernetes, Docker).

Use ioredis if:

  • You have an existing large codebase using ioredis and migration cost outweighs benefits.
  • You need Redis Cluster with minimal configuration boilerplate.
  • Your team is comfortable with callback patterns or wants flexibility to use both.
  • You're using defineCommand for custom Lua scripts heavily.
  • You need to support very old Node.js versions (ioredis v4.x still supports Node 8).

Use @upstash/redis if:

  • You're deploying serverless functions (AWS Lambda, Google Cloud Functions, Azure Functions).
  • You need to run on edge platforms (Cloudflare Workers, Vercel Edge, Deno Deploy).
  • You want HTTP-based access without managing TCP connections.
  • Your workload is bursty (unpredictable traffic spikes) and you want pay-per-use pricing.
  • You need automatic request deduplication for resilience.

Migration guide: ioredis to node-redis

If you're moving an existing ioredis project to node-redis, the main changes are:

  1. Explicit connection: Add await client.connect().
  2. Command capitalization: Change client.get() to client.GET() or client.get() (both work).
  3. Options as objects: Replace set('key', 'value', 'EX', 10) with set('key', 'value', { EX: 10 }).
  4. Pub/Sub: Replace client.on('message') with subscriber.subscribe() and dedicated connections.
  5. Pipelining: Replace pipeline() with multi() and exec().

Redis provides an official migration guide with side-by-side examples for every common scenario.

FAQs

Q: Will ioredis be deprecated? No. Redis Inc doesn't maintain ioredis, but the community actively maintains it, and it's stable. You won't be forced to migrate, but new feature development happens in node-redis first.

Q: Can I use node-redis with Upstash? Yes. Upstash provides standard Redis protocol endpoints; any client (node-redis, ioredis, @upstash/redis) works. Choose based on your runtime, not your database provider.

Q: Is @upstash/redis suitable for a production API? Yes, if your workload is sub-5K ops/sec. If you need higher throughput or lower latency, use a traditional TCP-based client (node-redis or ioredis) against your own Redis server or a managed provider like Redis Cloud, AWS ElastiCache, or Azure Cache.

Q: What's the latency difference between clients in real applications? 1-2ms at the 99th percentile. Network latency to your Redis server (5-50ms depending on geography) is the real bottleneck.

Q: Do I need to test client performance myself? Only if you're doing latency-sensitive operations (sub-millisecond SLA). For caching, rate limiting, queues, and sessions, all three clients are production-ready.

Q: Which client has the biggest ecosystem of integrations? node-redis. Popular frameworks like Next.js, Express.js, and NestJS have first-class integration examples. ioredis has strong community support but fewer official integrations.