Flect kv
Flect · KV stores
Section titled “Flect · KV stores”Part of the flect skill set.
What it is
Section titled “What it is”Flect KV stores are Redis-compatible, powered by Valkey. Each store has its
own key namespace. env.kv(binding) returns an official
ioredis client whose keys are transparently
prefixed, so stores never collide.
Create
Section titled “Create”flect kv create my-cache # in the scope of ./flect.toml; prints id = "res_…"flect kv list # id, name, status, and the services that use itflect kv status <id>flect kv delete <id> # refused while bound; --force unbinds first[[kv]]binding = "CACHE"id = "res_m2b8td"
[services.api]uses = ["CACHE"]Then list the binding in the uses of every service whose code calls it.
A service that does not list it cannot resolve it (403 BINDING_NOT_USED).
Use in code
Section titled “Use in code”import { createEnv } from '@dotlabshq/flect-sdk'
const env = createEnv()const cache = await env.kv('CACHE') // official ioredis client
// strings + TTL — note the ioredis signature: set(key, val, 'EX', seconds)await cache.set('session:abc', token, 'EX', 3600)const val = await cache.get('session:abc')await cache.del('session:abc')
// counters, lists, hashes, sets — full ioredis APIawait cache.incr('hits:today')await cache.lpush('queue:jobs', JSON.stringify(job))await cache.hset('config', 'theme', 'dark')Don’t invent methods — it’s the real ioredis. Use cache.set(k,v,'EX',ttl),
not setJson/getJson.
Common patterns
Section titled “Common patterns”Cache-aside
Section titled “Cache-aside”async function getCached<T>(key: string, load: () => Promise<T>, ttl = 300): Promise<T> { const hit = await cache.get(key) if (hit) return JSON.parse(hit) as T const data = await load() await cache.set(key, JSON.stringify(data), 'EX', ttl) return data}Invalidate on write (cache.del(key)) so the next read repopulates from the DB.
Rate limiting
Section titled “Rate limiting”async function underLimit(ip: string, limit = 100): Promise<boolean> { const key = `rl:${ip}:${Math.floor(Date.now() / 60000)}` // per minute const n = await cache.incr(key) if (n === 1) await cache.expire(key, 60) return n <= limit}Isolation
Section titled “Isolation”Keys are prefixed with the store’s namespace automatically — use short, natural
keys. KEYS/SCAN only ever see your store’s keys.
Local development
Section titled “Local development”flect dev # createEnv() resolves CACHE locallydocker run -d -p 6379:6379 valkey/valkey:8-alpine# VALKEY_URL=redis://localhost:6379 (default)