Getting Started (Browser)
The recached-edge package is the TypeScript SDK for the browser WASM client. It gives you a Cache class backed by the same core-engine as the server, with optional WebSocket sync to a Recached server instance.
Install
npm install recached-edge
# or
pnpm add recached-edge
# or
yarn add recached-edgeInitialize
Use createCache() — it initializes the WASM module and returns a ready Cache instance.
import { createCache } from 'recached-edge'
// Local-only, in-memory cache (no server connection)
const cache = await createCache()For bundlers that support top-level await (Vite, Next.js, modern webpack):
// lib/cache.ts — shared singleton
import { createCache } from 'recached-edge'
export const cache = await createCache()Connect to a server
Pass connect to createCache() to enable WebSocket sync. Omit it for local-only mode.
import { createCache } from 'recached-edge'
// Connect to the Recached server WebSocket port
const cache = await createCache({
connect: { url: 'ws://localhost:6380' },
})
// With TLS (production)
const cache = await createCache({
connect: { url: 'wss://cache.yourdomain.com:6380' },
})
// With auth (if RECACHED_PASSWORD is set on the server)
const cache = await createCache({
connect: { url: 'ws://localhost:6380', password: 'your-secret' },
})Once connected, any mutation from the server (SET, DEL, etc.) is automatically pushed to the local WASM store. Any local write is forwarded to the server and fanned out to other connected clients.
Basic usage
// Strings
cache.set('theme', 'dark')
console.log(cache.get('theme')) // 'dark'
// With expiry (seconds)
cache.setEx('session:token', 'abc123', 3600)
// Check existence and TTL
cache.exists('theme') // true
cache.ttl('session:token') // remaining seconds
// Delete
cache.del('theme')
cache.get('theme') // null
// React to any mutation (from any source — local, server, or other tabs)
const unsubscribe = cache.onMutation(() => {
const count = cache.get('cart:count')
console.log('Cart count is now:', count)
})
// Stop listening
unsubscribe()createCache options
import { createCache } from 'recached-edge'
const cache = await createCache({
// Enable IndexedDB persistence (survives page refresh)
persistence: true,
// BroadcastChannel name for cross-tab mutation sharing
broadcastChannel: 'my-app-cache',
// Connect to the Recached server WebSocket port
connect: {
url: 'ws://localhost:6380',
// Server password (if RECACHED_PASSWORD is set)
password: 'your-secret',
},
})All three options are independent — you can use persistence and cross-tab sync without a server connection.
React
If you are using React, install the official hooks package instead:
npm install @recached/reactimport { RecachedProvider, useKey } from '@recached/react'
function App() {
return (
<RecachedProvider options={{ connect: { url: 'ws://localhost:6380' } }}>
<CartBadge userId={42} />
</RecachedProvider>
)
}
function CartBadge({ userId }: { userId: number }) {
const count = useKey(`cart:${userId}:count`)
return <span className="badge">{count ?? '0'}</span>
}See the React hooks docs for the full guide.
Vue
If you are using Vue 3, install the official composables package instead:
npm install @recached/vue// main.ts
import { createApp } from 'vue'
import { RecachedPlugin } from '@recached/vue'
import App from './App.vue'
const app = createApp(App)
app.use(RecachedPlugin, { connect: { url: 'ws://localhost:6380' } })
app.mount('#app')<!-- CartBadge.vue -->
<script setup lang="ts">
import { useKey } from '@recached/vue'
const props = defineProps<{ userId: number }>()
const count = useKey(`cart:${props.userId}:count`)
</script>
<template>
<span class="badge">{{ count ?? '0' }}</span>
</template>See the Vue composables docs for the full guide.
Without a server (local-only cache)
Do not pass connect to createCache(). The WASM module runs as a pure in-memory cache with TTL — no server, no WebSocket, no backend changes required.
import { createCache } from 'recached-edge'
const cache = await createCache() // no connect option — local-only
async function getUser(id: number): Promise<User> {
const key = `user:${id}`
const cached = cache.getJSON<User>(key)
if (cached !== null) return cached
const user: User = await fetch(`/api/users/${id}`).then(r => r.json())
cache.setJSON(key, user, 60) // cache for 60s
return user
}
async function getProducts(): Promise<Product[]> {
const cached = cache.getJSON<Product[]>('products')
if (cached !== null) return cached
const products: Product[] = await fetch('/api/products').then(r => r.json())
cache.setJSON('products', products, 300) // cache for 5 minutes
return products
}
// Invalidate on mutation
async function updateUserName(id: number, name: string): Promise<void> {
await fetch(`/api/users/${id}`, {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name }),
})
cache.del(`user:${id}`) // next call to getUser() will refetch
}This pattern replaces the manual fetchedAt timestamp approach you might use with Zustand or Redux. TTL is declared once at write time; get() returns null automatically when the entry has expired.
Manual reactivity (non-React frameworks)
onMutation fires whenever the local store changes — from a local write, a server push, or a cross-tab BroadcastChannel message. It is the low-level hook used by useKey and useKeyJSON internally.
// Svelte
import { onMount, onDestroy } from 'svelte'
import { writable } from 'svelte/store'
import { cache } from '../lib/cache'
export let productId: string
const key = `stock:${productId}`
const stock = writable<string | null>(cache.get(key))
let unsubscribe: () => void
onMount(() => {
stock.set(cache.get(key))
unsubscribe = cache.onMutation(() => stock.set(cache.get(key)))
})
onDestroy(() => unsubscribe?.())The callback receives no arguments — it signals that something changed. Read the specific key you care about inside the callback.
Bundler configuration
Vite
Vite handles WASM imports natively. No extra config needed for most setups.
If you see issues with the WASM file not being served, add to vite.config.ts:
import { defineConfig } from 'vite'
export default defineConfig({
optimizeDeps: {
exclude: ['recached-edge'],
},
})Next.js (App Router)
// app/providers.tsx
'use client'
import { createCache } from 'recached-edge'
import { cache as cacheRef } from '../lib/cache'
export function CacheProvider({ children }: { children: React.ReactNode }) {
// WASM must be initialized in a client component (after hydration)
return <>{children}</>
}Use @recached/react for the recommended Next.js App Router integration — it wraps WASM init and the cache lifecycle automatically.
webpack
Add to your webpack config:
module.exports = {
experiments: {
asyncWebAssembly: true,
},
}