mcpField
B
bun-redis

bun-redis

github.com/secondskyData & databases240394 installsUpdated 5w ago
Repository

About bun-redis

A practical guide for developers using Redis with Bun through common Redis clients. It covers data structures, caching patterns, and session storage examples.

Key features

  1. 01Options for ioredis, @upstash/redis, and redis
  2. 02Strings and data structure operations
  3. 03RedisJSON, pub/sub, and transactions
  4. 04Examples for caching, rate limiting, and sessions

Use cases

  1. 01Read and write Redis data in Bun projects
  2. 02Add caching and rate limiting to an application
  3. 03Use Redis for sessions or queued tasks

Install

Copy the command and run it in a terminal.

$npx skills add https://github.com/secondsky/claude-skills --skill bun-redis

After installing, type/bun-redisin Claude Code to confirm it loaded.

Details

From the project README

Bun Redis

Redis integration with Bun using popular Redis clients.

Client Options

ClientBest ForInstall
ioredisSelf-hosted Redisbun add ioredis
@upstash/redisServerless/Edgebun add @upstash/redis
redisOfficial Node clientbun add redis

ioredis Setup

import Redis from "ioredis";

// Default connection
const redis = new Redis();

// With options
const redis = new Redis({
  host: "localhost",
  port: 6379,
  password: "secret",
  db: 0,
});

// Connection string
const redis = new Redis("redis://:password@localhost:6379/0");

// TLS connection
const redis = new Redis({
  host: "redis.example.com",
  port: 6380,
  tls: {},
});

Basic Operations

import Redis from "ioredis";

const redis = new Redis();

// Strings
await redis.set("name", "Alice");
await redis.set("count", "100");
await redis.setex("temp", 60, "expires in 60s"); // With TTL

const name = await redis.get("name"); // "Alice"
const count = await redis.incr("count"); // 101
await redis.del("name");

// Check existence
const exists = await redis.exists("name"); // 0 or 1

// TTL
await redis.expire("key", 3600); // Set 1 hour TTL
const ttl = await redis.ttl("key"); // Get remaining TTL

Data Structures

Hashes
// Set hash fields
await redis.hset("user:1", {
  name: "Alice",
  email: "[email protected]",
  age: "30",
});

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

// Get all fields
const user = await redis.hgetall("user:1");
// { name: "Alice", email: "...", age: "30" }

// Increment field
await redis.hincrby("user:1", "visits", 1);
Lists
// Add to list
await redis.rpush("queue", "task1", "task2");
await redis.lpush("queue", "urgent");

// Pop from list
const task = await redis.lpop("queue"); // "urgent"
const blocking = await redis.blpop("queue", 5); // Wait 5s

// Range
const items = await redis.lrange("queue", 0, -1);
Sets
// Add members
await redis.sadd("tags", "javascript", "typescript", "bun");

// Check membership
const isMember = await redis.sismember("tags", "bun"); // 1

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

// Set operations
await redis.sinter("tags1", "tags2"); // Intersection
await redis.sunion("tags1", "tags2"); // Union
Sorted Sets
// Add with scores
await redis.zadd("leaderboard", 100, "alice", 200, "bob", 150, "charlie");

// Get by rank
const top3 = await redis.zrevrange("leaderboard", 0, 2, "WITHSCORES");

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

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

JSON (RedisJSON)

// Requires RedisJSON module
await redis.call("JSON.SET", "user:1", "$", JSON.stringify({
  name: "Alice",
  settings: { theme: "dark" },
}));

const user = await redis.call("JSON.GET", "user:1");
const settings = await redis.call("JSON.GET", "user:1", "$.settings");

Pub/Sub

import Redis from "ioredis";

// Publisher
const pub = new Redis();

// Subscriber
const sub = new Redis();

// Subscribe to channel
sub.subscribe("notifications", (err, count) => {
  console.log(`Subscribed to ${count} channels`);
});

// Handle messages
sub.on("message", (channel, message) => {
  console.log(`${channel}: ${message}`);
});

// Publish
await pub.publish("notifications", JSON.stringify({
  type: "alert",
  message: "Hello!",
}));

// Pattern subscribe
sub.psubscribe("user:*");
sub.on("pmessage", (pattern, channel, message) => {
  console.log(`${pattern} -> ${channel}: ${message}`);
});

Transactions

// Multi/Exec
const results = await redis
  .multi()
  .set("key1", "value1")
  .set("key2", "value2")
  .incr("counter")
  .exec();

// Pipeline (no atomicity, better performance)
const pipeline = redis.pipeline();
pipeline.set("key1", "value1");
pipeline.set("key2", "value2");
pipeline.incr("counter");
const results = await pipeline.exec();

Upstash Redis (Serverless)

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

const redis = new Redis({
  url: process.env.UPSTASH_REDIS_REST_URL,
  token: process.env.UPSTASH_REDIS_REST_TOKEN,
});

// Same API as ioredis
await redis.set("key", "value");
const value = await redis.get("key");

// With automatic JSON serialization
await redis.set("user", { name: "Alice", age: 30 });
const user = await redis.get<{ name: string; age: number }>("user");

Caching Patterns

Cache-Aside
async function getUser(id: string) {
  // Check cache
  const cached = await redis.get(`user:${id}`);
  if (cached) {
    return JSON.parse(cached);
  }

  // Fetch from database
  const user = await db.query.users.findFirst({
    where: eq(users.id, id),
  });

  // Cache for 1 hour
  if (user) {
    await redis.setex(`user:${id}`, 3600, JSON.stringify(user));
  }

  return user;
}
Write-Through
async function updateUser(id: string, data: UserUpdate) {
  // Update database
  await db.update(users).set(data).where(eq(users.id, id));

  // Update cache
  const user = await db.query.users.findFirst({
    where: eq(users.id, id),
  });
  await redis.setex(`user:${id}`, 3600, JSON.stringify(user));

  return user;
}
Rate Limiting
async function rateLimit(key: string, limit: number, window: number) {
  const current = await redis.incr(key);

  if (current === 1) {
    await redis.expire(key, window);
  }

  return current <= limit;
}

// Usage
const allowed = await rateLimit(`rate:${userId}`, 100, 60);
if (!allowed) {
  throw new Error("Rate limit exceeded");
}

Session Storage

import { Hono } from "hono";
import Redis from "ioredis";
import { v4 as uuid } from "uuid";

const redis = new Redis();
const app = new Hono();

app.use("*", async (c, next) => {
  const sessionId = c.req.header("X-Session-Id") || uuid();
  const session = await redis.hgetall(`session:${sessionId}`);

  c.set("session", session);
  c.set("sessionId", sessionId);

  await next();

  // Save session
  const updatedSession = c.get("session");
  if (Object.keys(updatedSession).length > 0) {
    await redis.hset(`session:${sessionId}`, updatedSession);
    await redis.expire(`session:${sessionId}`, 86400); // 24h
  }
});

Common Errors

ErrorCauseFix
ECONNREFUSEDRedis not runningStart Redis server
NOAUTHAuthentication requiredProvide password
WRONGTYPEWrong data typeCheck key type
OOMOut of memoryConfigure maxmemory

When to Load References

Load references/clustering.md when:

  • Redis Cluster setup
  • Sentinel configuration
  • High availability patterns

Load references/lua-scripts.md when:

  • Custom Lua scripts
  • Atomic operations
  • Complex transactions

Comments

Markdown supported