Production-ready Redis wrapper for Bun with namespaces, controllers, and TypeScript support
npm install @codecaine/bun-redis-wrapper


A powerful Redis wrapper for Bun with production-ready controllers for building real applications. Drop-in solutions for sessions, caching, rate limiting, job queues, and more.
``typescript
import { createRedis } from "@codecaine/bun-redis-wrapper";
import { SessionController, CacheController } from "@codecaine/bun-redis-wrapper/controllers";
const redis = await createRedis();
// Sessions - ready to use!
const sessions = new SessionController(redis);
const sessionId = await sessions.create("user-123", { name: "Alice" });
// Caching - intelligent and automatic!
const cache = new CacheController(redis);
const user = await cache.getOrSet("user:123", () => db.getUser(123), 300);
`
š¦ Available Controllers:
- SessionController - User sessions with multi-device support
- CacheController - Intelligent caching with cache-aside pattern
- RateLimiterController - API rate limiting (fixed/sliding window, token bucket)
- QueueController - Background job processing with priorities
- StorageController - Simple key-value storage with JSON
- AnalyticsController - Metrics tracking with HyperLogLog
- PubSubController - Publish/subscribe messaging
- LockController - Distributed locks (simple mutex)
- LeaderboardController - Rankings and leaderboards (sorted sets)
- CounterController - Atomic counters and stats
- FormularyController - Example domain controller (healthcare formulary)
- RadAppController - Example domain controller (RAD app patterns)
š Controllers Documentation - Complete guide with examples
š Example Application - Full app using all controllers
`typescript
import { createRedis, createNamespacedRedis } from "@codecaine/bun-redis-wrapper";
await using redis = await createRedis("redis://localhost:6379");
await redis.set("key", "value");
// With namespaces
const authApp = createNamespacedRedis(redis, "auth");
const shopApp = createNamespacedRedis(redis, "shop");
`
ā
Production-Ready Controllers - Drop-in solutions for common use cases ā NEW
ā
Beginner Friendly - Clear documentation and examples ā NEW
ā
Type-Safe - Full TypeScript support with proper types
ā
Namespace Support - Automatic key prefixing to prevent collisions
ā
Comprehensive API - All common Redis operations supported
ā
JSON Helpers - Built-in JSON serialization/deserialization
ā
Sorted Sets - Leaderboards, rankings, and priority queues
ā
Streams - Event sourcing and real-time data processing
ā
Geospatial - Location-based queries and proximity search
ā
HyperLogLog - Memory-efficient unique counting
ā
Async Dispose - Works with await using syntax
ā
Zero Dependencies - Uses Bun's native RedisClient
- Strings - GET, SET, INCR, DECR
- Hashes - HGET, HSET, HGETALL
- Lists - LPUSH, RPUSH, LRANGE
- Sets - SADD, SREM, SMEMBERS
- Sorted Sets - ZADD, ZRANGE, ZRANK (leaderboards)
- Streams - XADD, XREAD, XRANGE (event logs)
- Geospatial - GEOADD, GEODIST, GEORADIUS
- HyperLogLog - PFADD, PFCOUNT (unique counting)
Run all demos locally:
`bash`
bun run demos
`bashUsing Bun
bun add @codecaine/bun-redis-wrapper
š Quick Examples
$3
`typescript
import { SessionController } from "@codecaine/bun-redis-wrapper/controllers";const sessions = new SessionController(redis);
// Create session
const sessionId = await sessions.create("user-123", {
name: "Alice",
email: "alice@example.com"
});
// Validate session
const session = await sessions.validate(sessionId);
if (session) {
console.log(
Welcome ${session.data.name}!);
}// Logout
await sessions.destroy(sessionId);
`$3
`typescript
import { CacheController } from "@codecaine/bun-redis-wrapper/controllers";const cache = new CacheController(redis);
// Cache-aside pattern (automatic!)
const user = await cache.getOrSet(
"user:123",
async () => await database.getUser(123), // Only called on cache miss
300 // Cache for 5 minutes
);
// Check cache stats
const stats = await cache.getStats();
console.log(
Hit rate: ${stats.hitRate}%);
`$3
`typescript
import { RateLimiterController } from "@codecaine/bun-redis-wrapper/controllers";const limiter = new RateLimiterController(redis);
// Check rate limit (10 requests per minute)
const result = await limiter.check("user-123", 10, 60);
if (!result.allowed) {
throw new Error(
Rate limited. Retry after ${result.retryAfter}s);
}console.log(
${result.remaining} requests remaining);
`$3
`typescript
import { QueueController } from "@codecaine/bun-redis-wrapper/controllers";const queue = new QueueController(redis);
// Add job
await queue.add("send-email", {
to: "user@example.com",
subject: "Welcome!"
}, { priority: 8 });
// Process jobs (in worker)
const job = await queue.next();
if (job) {
await sendEmail(job.data);
await queue.complete(job.id);
}
`$3
`$3
`typescript
import { createRedis, createNamespacedRedis } from "@codecaine/bun-redis-wrapper";await using redis = await createRedis();
// Each tenant gets isolated namespace
const tenant1 = createNamespacedRedis(redis, "tenant:acme");
const tenant2 = createNamespacedRedis(redis, "tenant:globex");
await tenant1.setJSON("config", { theme: "dark" });
await tenant2.setJSON("config", { theme: "light" });
// No collisions - each tenant has separate data
`$3
`typescript
const env = process.env.NODE_ENV || "development";
const namespace = myapp:${env};
const redis = createNamespacedRedis(baseRedis, namespace);// Keys stored as: "myapp:development:..." or "myapp:production:..."
`API Reference
$3
####
createRedis(url?: string)Creates a base Redis connection.
`typescript
await using redis = await createRedis("redis://localhost:6379");
`####
createNamespacedRedis(redis, namespace)Creates a namespaced wrapper with automatic key prefixing.
`typescript
const app = createNamespacedRedis(redis, "myapp");
`####
clearNamespace(redis, namespace)Deletes all keys in a namespace.
`typescript
const deleted = await clearNamespace(redis, "myapp");####
copyNamespace(redis, fromNamespace, toNamespace, options?)Copies all keys from one namespace to another (useful for dev ā staging ā prod promotions within the same Redis instance).
`typescript
const result = await copyNamespace(redis, "myapp:dev", "myapp:staging", { replace: false });
console.log(result); // { scanned, copied, skipped }
`
`$3
All operations support both the base
RedisWrapper and NamespacedRedisWrapper:Core Operations
-
get(key), set(key, value, options?)
- del(...keys), exists(...keys)JSON Operations
-
getJSON, setJSONMulti Operations
-
mget(...keys), mset(data)Hash Operations
-
hget(key, field), hset(key, field, value)
- hmget(key, ...fields), hmset(key, data)
- hgetAll(key)Counter Operations
-
incr(key), decr(key)TTL Operations
-
ttl(key), setTTL(key, seconds), expire(key, seconds)Pattern Matching
-
scanAll(pattern, count?)Pub/Sub
-
subscribe(channel, callback), publish(channel, message)List Operations
-
lpush(key, ...values), rpush(key, ...values)
- lrange(key, start?, stop?), lpop(key), rpop(key)Set Operations
-
sadd(key, ...members), srem(key, ...members), smembers(key)Examples
$3
`typescript
interface UserSession {
userId: number;
username: string;
loginAt: number;
}const sessions = createNamespacedRedis(redis, "sessions");
const session: UserSession = {
userId: 123,
username: "alice",
loginAt: Date.now()
};
// Store with 1 hour expiration
await sessions.setJSON("user:123", session, { EX: 3600 });
// Retrieve
const active = await sessions.getJSON("user:123");
`$3
`typescript
async function checkRateLimit(
client: NamespacedRedisWrapper,
userId: string,
maxRequests: number,
windowSeconds: number
): Promise {
const key = ratelimit:${userId};
const count = await client.incr(key);
if (count === 1) {
await client.setTTL(key, windowSeconds);
}
return count <= maxRequests;
}const api = createNamespacedRedis(redis, "api:v1");
const allowed = await checkRateLimit(api, "user:123", 10, 60);
`$3
`typescript
const app = createNamespacedRedis(redis, "myapp");// Store user sessions
await app.set("session:user:1", "data1");
await app.set("session:user:2", "data2");
await app.set("config:timeout", "30");
// Find all sessions (scoped to namespace)
const sessionKeys = await app.scanAll("session:*");
console.log(sessionKeys); // ["session:user:1", "session:user:2"]
`Best Practices
$3
`typescript
// ā
Good: Clear hierarchy with colons
"app:entity:id:field"
"myapp:user:12345:profile"// ā Avoid: Inconsistent separators
"myapp_user_12345"
`$3
`typescript
// ā
Good: One connection, multiple namespaces
await using redis = await createRedis();
const app1 = createNamespacedRedis(redis, "app1");
const app2 = createNamespacedRedis(redis, "app2");// ā Bad: Multiple connections
const redis1 = await createRedis();
const redis2 = await createRedis();
`$3
`typescript
import { test, afterEach } from "bun:test";afterEach(async () => {
await clearNamespace(redis, "test");
});
`Use Cases
- š¢ Microservices - Isolate data per service
- š„ Multi-tenant SaaS - Separate data per customer
- š Environment Isolation - dev/staging/prod separation
- š API Versioning - v1/v2 namespace isolation
- šØāš» Team Resources - Team-based access control
Performance
The namespace wrapper adds minimal overhead - just string concatenation. No additional network calls or memory overhead.
š Testing
Run the comprehensive test suite:
`bash
Run all tests
bun testRun tests in watch mode
bun test --watch
``The test suite includes 83 tests covering:
- Core Redis operations
- JSON operations
- Multi operations
- Hash operations
- Counter operations
- TTL operations
- Pattern matching
- List operations
- Set operations
- Namespace isolation
- Multi-tenant scenarios
- Integration tests
- Bun v1.0+
- Redis server
MIT
---