Redis cache
A dedicated, per-project Redis instance for caching, ephemeral data, and rate-limit counters. Available on the Pro and Business plans. Treat it as a cache, not a database — keys are evicted when memory fills up and nothing is persisted to disk.
Enabling Redis
Redis is a paid feature. Free-plan projects share a single VPS where a per-project Redis container doesn't fit the resource budget, so the feature is gated to Pro and Business.
Turn it on from the project dashboard under Project → Redis. There you pick a memory size (Redis maxmemory) within your plan's range:
Plan Memory range Default
Pro 16 MB – 256 MB 64 MB
Business 16 MB – 1024 MB 256 MB
Eviction policy: allkeys-lru (oldest keys drop first when full)
Persistence: none (in-memory only)Where Redis runs — server-side only
The @ichibase/redisSDK connects to your project's Redis over a raw connection URL of the form redis://:<password>@host:6379. That password is a privileged secret with full read/write access to your cache — it is not a scoped ich_pub_… client key and there is no per-key access control like RLS.
ICHIBASE_REDIS_URLand never leaves the server. To let a client touch the cache, front it with an Edge Function that validates the caller and performs the Redis operation on their behalf — see Edge Functions.Inside an Edge Function you don't pass any connection details: createRedis() reads ICHIBASE_REDIS_URL from the environment automatically. Elsewhere (your own server, a worker, a script) pass the URL or host/port/password explicitly.
Connecting
import { createRedis } from 'jsr:@ichibase/redis';
// Inside an Edge Function: ICHIBASE_REDIS_URL is set for you.
const redis = await createRedis();
// Outside the platform: pass the URL (or host/port/password) yourself.
const redis2 = await createRedis({
url: Deno.env.get('ICHIBASE_REDIS_URL'),
});
// Long-lived workers: keep one client; call redis.close() on shutdown.Reading and writing
createRedis() returns a standard Redis client (the @db/redisdriver), so you get the familiar command set. The examples below use the commands you'll reach for most.
const redis = await createRedis();
// Strings
await redis.set('greeting', 'hello');
const greeting = await redis.get('greeting'); // "hello"
// Set with a TTL (seconds) — value auto-expires
await redis.set('otp:42', '123456', { ex: 300 }); // 5 minutes
// Counters
await redis.incr('views:home'); // 1, 2, 3, …
await redis.incrby('views:home', 10); // jump by 10
// Expiry on an existing key, and time-to-live lookup
await redis.expire('session:abc', 3600); // 1 hour
const ttl = await redis.ttl('session:abc'); // seconds left
// Delete
await redis.del('greeting');
// Done — close in short-lived contexts
await redis.close();Pattern: cache-aside
The most common pattern. Check the cache first; on a miss, read from the source of truth, then store the result with a TTL so it refreshes on its own.
// Inside an Edge Function
import { createIchibase } from 'jsr:@ichibase/edge';
import { createRedis } from 'jsr:@ichibase/redis';
const ichi = createIchibase();
const redis = await createRedis();
async function getProfile(id: string) {
const cacheKey = `profile:${id}`;
// 1. Try the cache
const hit = await redis.get(cacheKey);
if (hit) return JSON.parse(hit);
// 2. Miss — read from the database
const { data } = await ichi.from('profiles').select('*').eq('id', id).single();
// 3. Populate the cache for next time (10-minute TTL)
if (data) await redis.set(cacheKey, JSON.stringify(data), { ex: 600 });
return data;
}Pattern: rate-limit counter
Increment a per-caller key and set its expiry on the first hit. When the count crosses your threshold inside the window, reject the request.
async function allow(ip: string, limit = 60, windowSec = 60) {
const key = `rl:${ip}`;
const count = await redis.incr(key);
if (count === 1) {
// First request in this window — start the clock
await redis.expire(key, windowSec);
}
return count <= limit;
}
if (!(await allow(clientIp))) {
return new Response('Too Many Requests', { status: 429 });
}Keep in mind
Redis here is ephemeral. With the allkeys-lrueviction policy, when the cache reaches its memory cap Redis drops the least-recently-used keys to make room — even keys that haven't expired yet. There is no disk persistence, so a container restart or a memory resize clears everything.
Design for it: always set a TTL where it makes sense, never treat Redis as the only copy of important data, and make sure your code works when a key isn't there.
