Skip to main content
Bun includes a built-in Redis (and Valkey) client that provides a fast, promise-based API for interacting with Redis servers.

Quick Start

const redis = new Bun.RedisClient();

await redis.set("hello", "world");
const value = await redis.get("hello");
console.log(value); // "world"

await redis.close();

Creating a Client

Default Connection

// Connects to $VALKEY_URL, $REDIS_URL, or "valkey://localhost:6379"
const redis = new Bun.RedisClient();

Custom URL

const redis = new Bun.RedisClient("redis://localhost:6379");
// or
const redis = new Bun.RedisClient("valkey://localhost:6379");

With Options

const redis = new Bun.RedisClient("redis://localhost:6379", {
  connectionTimeout: 5000, // ms
  idleTimeout: 0, // No timeout
  autoReconnect: true,
  maxRetries: 10,
  enableOfflineQueue: true,
  enableAutoPipelining: true,
});

With TLS

const redis = new Bun.RedisClient("rediss://localhost:6380", {
  tls: true,
  // or with options
  tls: {
    ca: Bun.file("./ca.pem"),
    cert: Bun.file("./cert.pem"),
    key: Bun.file("./key.pem"),
  },
});

Connection Management

Connect

const redis = new Bun.RedisClient();

// Explicit connect
await redis.connect();

// Or use callbacks
redis.onconnect = () => {
  console.log("Connected to Redis");
};

redis.onclose = (error) => {
  console.log("Disconnected:", error);
};

Close

redis.close();

Connection Status

if (redis.connected) {
  console.log("Connected");
}

const buffered = redis.bufferedAmount;
console.log(`${buffered} bytes buffered`);

String Commands

GET and SET

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

// Get as Buffer
const buffer = await redis.getBuffer("key");

SET with Options

// Expire in seconds
await redis.set("key", "value", "EX", 60);

// Expire in milliseconds
await redis.set("key", "value", "PX", 60000);

// Only if doesn't exist
await redis.set("key", "value", "NX");

// Only if exists
await redis.set("key", "value", "XX");

// Return old value
const old = await redis.set("key", "new", "GET");

// Keep TTL
await redis.set("key", "value", "KEEPTTL");

Increment/Decrement

await redis.set("counter", "0");

await redis.incr("counter"); // 1
await redis.incrby("counter", 5); // 6
await redis.decr("counter"); // 5
await redis.decrby("counter", 3); // 2

// Float increment
const result = await redis.incrbyfloat("price", 1.5);
console.log(result); // "1.5"

Other String Commands

// Append
await redis.append("key", " more");

// Get range
const substr = await redis.getrange("key", 0, 4);

// Set range
await redis.setrange("key", 6, "text");

// String length
const len = await redis.strlen("key");

// Get and delete
const value = await redis.getdel("key");

// Multiple get/set
await redis.mset(["key1", "val1", "key2", "val2"]);
const values = await redis.mget(["key1", "key2"]);

Key Commands

Existence and Deletion

// Check existence
if (await redis.exists("key")) {
  console.log("Key exists");
}

// Delete keys
const deleted = await redis.del("key1", "key2");
console.log(`Deleted ${deleted} keys`);

// Unlink (async delete)
await redis.unlink("key1", "key2");

Expiration

// Expire in seconds
await redis.expire("key", 60);

// Expire at timestamp (seconds)
await redis.expireat("key", Date.now() / 1000 + 60);

// Expire in milliseconds
await redis.pexpire("key", 60000);

// Expire at timestamp (milliseconds)
await redis.pexpireat("key", Date.now() + 60000);

// Get TTL
const ttl = await redis.ttl("key"); // seconds
const pttl = await redis.pttl("key"); // milliseconds

// Remove expiration
await redis.persist("key");

Other Key Commands

// Rename
await redis.rename("oldkey", "newkey");

// Type
const type = await redis.type("key");

// Random key
const key = await redis.randomkey();

// Keys matching pattern
const keys = await redis.keys("user:*");

Hash Commands

// Set field
await redis.hset("user:1", "name", "Alice");

// Set multiple fields
await redis.hmset("user:1", { name: "Alice", email: "[email protected]" });

// Get field
const name = await redis.hget("user:1", "name");

// Get multiple fields
const fields = await redis.hmget("user:1", ["name", "email"]);

// Get all fields
const user = await redis.hgetall("user:1");

// Check field exists
if (await redis.hexists("user:1", "name")) {
  console.log("Field exists");
}

// Delete fields
await redis.hdel("user:1", ["email"]);

// Get all keys/values
const keys = await redis.hkeys("user:1");
const values = await redis.hvals("user:1");

// Field count
const count = await redis.hlen("user:1");

// Increment field
await redis.hincrby("user:1", "visits", 1);
await redis.hincrbyfloat("user:1", "balance", 10.5);

List Commands

// Push
await redis.lpush("queue", "item1", "item2");
await redis.rpush("queue", "item3");

// Pop
const left = await redis.lpop("queue");
const right = await redis.rpop("queue");

// Blocking pop
const item = await redis.blpop(["queue"], 5); // timeout 5s

// Range
const items = await redis.lrange("queue", 0, -1);

// Length
const length = await redis.llen("queue");

// Index
const item = await redis.lindex("queue", 0);

// Set
await redis.lset("queue", 0, "new value");

// Trim
await redis.ltrim("queue", 0, 99);

Set Commands

// Add members
await redis.sadd("tags", "javascript", "typescript");

// Remove members
await redis.srem("tags", "typescript");

// Check membership
if (await redis.sismember("tags", "javascript")) {
  console.log("Is member");
}

// Get all members
const members = await redis.smembers("tags");

// Cardinality
const count = await redis.scard("tags");

// Random member
const random = await redis.srandmember("tags");

// Pop random
const popped = await redis.spop("tags");

// Set operations
const union = await redis.sunion(["set1", "set2"]);
const inter = await redis.sinter(["set1", "set2"]);
const diff = await redis.sdiff(["set1", "set2"]);

Sorted Set Commands

// Add members with scores
await redis.zadd("leaderboard", 100, "alice");
await redis.zadd("leaderboard", 200, "bob");

// Get score
const score = await redis.zscore("leaderboard", "alice");

// Increment score
await redis.zincrby("leaderboard", 10, "alice");

// Range by rank
const top = await redis.zrange("leaderboard", 0, 9);

// Range by score
const range = await redis.zrangebyscore("leaderboard", 100, 200);

// Rank
const rank = await redis.zrank("leaderboard", "alice");

// Cardinality
const count = await redis.zcard("leaderboard");

// Remove
await redis.zrem("leaderboard", "alice");

Pub/Sub

// Subscribe to channels
await redis.subscribe("channel1");

// Publish message
await redis.publish("channel1", "Hello");

// Unsubscribe
await redis.unsubscribe("channel1");

// Pattern subscribe
await redis.psubscribe("news:*");
await redis.punsubscribe("news:*");

Transactions

Use pipelining for atomic operations:
// Commands are automatically pipelined
const [result1, result2] = await Promise.all([
  redis.set("key1", "value1"),
  redis.set("key2", "value2"),
]);

Raw Commands

// Send custom command
const result = await redis.send("COMMAND", ["arg1", "arg2"]);

Type Signatures

class RedisClient {
  constructor(url?: string, options?: RedisOptions);
  
  connect(): Promise<void>;
  close(): void;
  
  readonly connected: boolean;
  readonly bufferedAmount: number;
  
  onconnect: ((this: RedisClient) => void) | null;
  onclose: ((this: RedisClient, error: Error) => void) | null;
  
  // String commands
  get(key: string | ArrayBufferView | Blob): Promise<string | null>;
  getBuffer(key: string | ArrayBufferView | Blob): Promise<Uint8Array | null>;
  set(key: string | ArrayBufferView | Blob, value: string | ArrayBufferView | Blob): Promise<"OK">;
  
  // Many more commands...
  
  send(command: string, args: string[]): Promise<any>;
}

interface RedisOptions {
  connectionTimeout?: number;
  idleTimeout?: number;
  autoReconnect?: boolean;
  maxRetries?: number;
  enableOfflineQueue?: boolean;
  enableAutoPipelining?: boolean;
  tls?: boolean | TLSOptions;
}

Build docs developers (and LLMs) love