Redis

Port 6379 Cache, sessions, rate limits, pub/sub, queues.

Connection

REDIS_URL=redis://:PASSWORD@db.bizfylabs.com:6379
# App ACL user (recommended):
REDIS_URL=redis://myapp_user:PASSWORD@db.bizfylabs.com:6379

Key Naming

Prefix every key with your app name: myapp:category:id

myapp:session:abc123
myapp:cache:user:42
myapp:ratelimit:1.2.3.4
myapp:queue:emails

Setup

npm install redis
import { createClient } from 'redis';
const client = createClient({ url: process.env.REDIS_URL });
await client.connect();

CRUD — Strings

Used for: sessions, simple cache, counters, locks.

// CREATE / SET
await client.set('myapp:session:token', JSON.stringify({ userId: 42 }));
await client.set('myapp:cache:products', data, { EX: 300 }); // TTL 5 min
await client.set('myapp:lock:job1', '1', { NX: true, EX: 30 }); // distributed lock

// READ
const raw = await client.get('myapp:session:token');
const session = raw ? JSON.parse(raw) : null;
const exists = await client.exists('myapp:session:token');

// UPDATE (overwrite)
await client.set('myapp:session:token', JSON.stringify({ userId: 99 }));

// DELETE
await client.del('myapp:session:token');
await client.del(['myapp:key1', 'myapp:key2']); // multiple

CRUD — Hashes

Used for: structured objects (user profile, settings).

// CREATE
await client.hSet('myapp:user:42', {
  name: 'Alice', email: 'a@b.com', lastLogin: new Date().toISOString()
});

// READ
const user = await client.hGetAll('myapp:user:42');
const name = await client.hGet('myapp:user:42', 'name');

// UPDATE
await client.hSet('myapp:user:42', 'name', 'Alice Updated');
await client.hIncrBy('myapp:user:42', 'loginCount', 1);

// DELETE field or whole hash
await client.hDel('myapp:user:42', 'lastLogin');
await client.del('myapp:user:42');

CRUD — Lists (Queues)

// CREATE (push)
await client.lPush('myapp:queue:emails', JSON.stringify({ to: 'a@b.com' }));
await client.rPush('myapp:queue:emails', JSON.stringify({ to: 'b@c.com' }));

// READ
const job = await client.rPop('myapp:queue:emails');
const peek = await client.lRange('myapp:queue:emails', 0, 9);
const len = await client.lLen('myapp:queue:emails');

// DELETE
await client.lRem('myapp:queue:emails', 1, job);

CRUD — Sets

// CREATE
await client.sAdd('myapp:online:users', '42', '99');

// READ
const members = await client.sMembers('myapp:online:users');
const isOnline = await client.sIsMember('myapp:online:users', '42');

// DELETE
await client.sRem('myapp:online:users', '42');

CRUD — Sorted Sets

// CREATE
await client.zAdd('myapp:leaderboard', { score: 1500, value: 'player1' });

// READ (top 10)
const top = await client.zRangeWithScores('myapp:leaderboard', 0, 9, { REV: true });

// UPDATE score
await client.zIncrBy('myapp:leaderboard', 50, 'player1');

// DELETE
await client.zRem('myapp:leaderboard', 'player1');

Scan Keys (safe alternative to KEYS *)

for await (const key of client.scanIterator({ MATCH: 'myapp:session:*', COUNT: 100 })) {
  console.log(key);
}

Rate Limiting Pattern

async function checkRateLimit(userId) {
  const key = `myapp:ratelimit:${userId}`;
  const count = await client.incr(key);
  if (count === 1) await client.expire(key, 60);
  return count <= 100;
}

Admin — Create ACL User

Platform admin only. Users are scoped to key prefix myapp:*.
docker exec -it bizfy-redis redis-cli -a "$REDIS_PASSWORD"

# Read/write user — can only touch myapp:* keys
ACL SETUSER myapp_user on >strong-random-password ~myapp:* +@all -@dangerous

# Read-only user
ACL SETUSER myapp_readonly on >strong-random-password ~myapp:* +@read -@dangerous

# Verify
ACL LIST
ACL GETUSER myapp_user

# Delete user
ACL DELUSER myapp_user

ACL Command Reference

CommandMeaning
onEnable the user
>passwordSet password
~myapp:*Only keys matching this pattern
+@all -@dangerousAll commands except dangerous ones
+@readRead-only commands

Send to Developer

REDIS_URL=redis://myapp_user:PASSWORD@db.bizfylabs.com:6379