Silgi

Storage

Key-value storage for cache, sessions, files — Redis, Cloudflare KV, S3, and 30+ backends via unstorage.

Silgi includes unstorage — a universal key-value storage layer with 30+ drivers. Configure storage once, use it everywhere: caching, sessions, file uploads, feature flags, queues.

Setup

Mount drivers by path prefix. Each prefix routes to a different backend:

server.ts
import {  } from 'silgi'
import  from 'unstorage/drivers/redis'
import  from 'unstorage/drivers/memory'
import  from 'unstorage/drivers/s3'

const  = ({
  : () => ({  }),
  : {
    : ({ : 'redis://localhost:6379' }),
    : (),
    : ({
      : 'uploads',
      : 'eu-west-1',
      : ..!,
      : ..!,
    }),
  },
})

Keys are routed by prefix:

storage.setItem('cache:users', data)      → Redis
storage.setItem('sessions:abc123', token) → Memory
storage.setItem('files:avatar.png', buf)  → S3

Using in procedures

Access storage via s.useStorage():

const  = s.$input(z.object({ : z.string() })).$resolve(async ({  }) => {
  const  = s.useStorage('sessions')
  const  = await .getItem(.token)
  if (!) return { : false }
  return { : true, :  }
})

const  = s.$input(z.object({ : z.string(), : z.string() })).$resolve(async ({  }) => {
  const  = s.useStorage('files')
  await .setItem(.name, .data)
  return { : true }
})

Cache integration

When you configure a cache mount, cacheQuery() uses it automatically — no setCacheStorage() needed:

import {  } from 'silgi/cache'

const  = silgi({
  : () => ({  }),
  : {
    : redisDriver({ : 'redis://localhost' }),
  },
})

// cacheQuery automatically uses the 'cache' mount → Redis
const  = .$use(({ : 60 })).$resolve(({  }) => .db.users.findMany())

Without a cache mount, cacheQuery() falls back to in-memory (default).

Analytics integration

When you configure a data mount, analytics automatically persists request and error entries to it. Data survives restarts and is pruned by retentionDays:

import  from 'unstorage/drivers/fs'

const  = silgi({
  : () => ({}),
  : {
    : ({ : '.data' }),
  },
})

.serve(appRouter, {
  : { : 90 },
})

Without a data mount, analytics falls back to in-memory (default).

Available drivers

All unstorage drivers work. Import from unstorage/drivers/*:

DriverImportUse case
Memoryunstorage/drivers/memoryDevelopment, testing, sessions
Redisunstorage/drivers/redisCache, sessions, pub/sub
Filesystemunstorage/drivers/fsConfig files, local storage
S3unstorage/drivers/s3File uploads, media
Cloudflare KVunstorage/drivers/cloudflare-kv-bindingEdge cache, config
Vercel KVunstorage/drivers/vercelServerless cache
MongoDBunstorage/drivers/mongodbDocument storage
LRU Cacheunstorage/drivers/lru-cacheIn-memory with eviction
HTTPunstorage/drivers/httpRemote storage proxy
Overlayunstorage/drivers/overlayMulti-tier (L1 memory → L2 Redis)

Multi-tier cache (overlay)

Layer memory in front of Redis for the fastest reads:

import  from 'unstorage/drivers/overlay'
import  from 'unstorage/drivers/memory'
import  from 'unstorage/drivers/redis'

const  = silgi({
  : () => ({}),
  : {
    : ({
      : [(), ({ : 'redis://localhost' })],
    }),
  },
})

Read: checks memory first, then Redis. Write: writes to both. Best of both worlds.

Pre-built storage instance

If you need full control, pass a pre-built unstorage instance:

import {  } from 'silgi/unstorage'
import  from 'unstorage/drivers/redis'

const  = ({})
.('cache', ({ : 'redis://localhost' }))
.('data', ({ : 'redis://localhost', : 'app' }))

const  = silgi({
  : () => ({}),
  ,
})

API reference

const  = s.useStorage('cache')

await .getItem('key') // Get value
await .setItem('key', value) // Set value
await .removeItem('key') // Delete
await .hasItem('key') // Check existence
await .getKeys() // List all keys
await .clear() // Delete all

Storage credentials (Redis URL, S3 keys) stay in the config — they never leak to procedures, context, or client responses. Only the key-value API is exposed.

What's next?

  • CachecacheQuery() with TTL, SWR, and invalidation
  • Plugins — all available plugins

On this page