KV Store
What It Is
Section titled “What It Is”Flect KV stores are Redis-compatible, powered by Valkey. Each KV store gets a key prefix enforced at the proxy layer — apps can only access their own keys even though all stores share the same Valkey instance.
Compatible with: ioredis, node-redis, any Redis client.
Create a KV Store
Section titled “Create a KV Store”flect kv create <name> # e.g. flect kv create my-cacheflect kv list # show KV stores in current envBind to an App
Section titled “Bind to an App”In flect.toml:
[[kv]]binding = "CACHE" # becomes CACHE_URL env var in the appname = "my-cache" # KV slug from flect kv listMultiple stores:
[[kv]]binding = "SESSION_STORE"name = "sessions"
[[kv]]binding = "RATE_LIMIT"name = "rate-limiter"Use in Code
Section titled “Use in Code”Install SDK: npm install @flect/sdk
import { kv } from '@flect/sdk'
// kv() returns an ioredis client, pre-configured from CACHE_URLconst cache = kv()
// String operationsawait cache.set('user:123', JSON.stringify(user))await cache.set('session:abc', token, 'EX', 3600) // expires in 1hconst val = await cache.get('user:123')await cache.del('session:abc')
// Countersawait cache.incr('hits:today')const count = await cache.get('hits:today')
// Listsawait cache.lpush('queue:jobs', JSON.stringify(job))const next = await cache.rpop('queue:jobs')
// Hash mapsawait cache.hset('config', 'theme', 'dark', 'lang', 'tr')const theme = await cache.hget('config', 'theme')
// Setsawait cache.sadd('active:users', userId)const isActive = await cache.sismember('active:users', userId)
// TTLawait cache.expire('temp:key', 300) // 5 minutesconst ttl = await cache.ttl('temp:key')With Raw ioredis
Section titled “With Raw ioredis”import Redis from 'ioredis'
const cache = new Redis(process.env['CACHE_URL']!)
// Keys are automatically prefixed by flect-proxy — use short keysawait cache.set('my-key', 'value') // stored as <prefix>:my-key in ValkeyCommon Patterns
Section titled “Common Patterns”Session Storage
Section titled “Session Storage”import { kv } from '@flect/sdk'
const store = kv()
async function createSession(userId: string): Promise<string> { const sessionId = crypto.randomUUID() await store.set(`session:${sessionId}`, userId, 'EX', 86400) // 24h return sessionId}
async function getSession(sessionId: string): Promise<string | null> { return store.get(`session:${sessionId}`)}Rate Limiting
Section titled “Rate Limiting”async function checkRateLimit(ip: string, limit = 100): Promise<boolean> { const key = `rl:${ip}:${Math.floor(Date.now() / 60000)}` // per minute const count = await store.incr(key) if (count === 1) await store.expire(key, 60) return count <= limit}Caching
Section titled “Caching”async function getCached<T>(key: string, fetch: () => Promise<T>, ttl = 300): Promise<T> { const cached = await store.get(key) if (cached) return JSON.parse(cached) as T const data = await fetch() await store.set(key, JSON.stringify(data), 'EX', ttl) return data}Local Development
Section titled “Local Development”# .env.local — local Redis/Valkey or redis-memoryCACHE_URL=redis://localhost:6379
# Or use a local Valkey via Dockerdocker run -d -p 6379:6379 valkey/valkey:8-alpineKey Isolation
Section titled “Key Isolation”Keys are automatically prefixed at the proxy layer. You do not need to manually prefix keys — KEYS * and SCAN also only return keys belonging to your KV store.
Limits
Section titled “Limits”- All Redis-compatible commands supported
- Key prefix isolation enforced at proxy — no cross-store access possible
- Max memory: shared Valkey instance, configured per environment