7. Persisting state
The cart already survives navigation — click around and it stays full. That’s the store doing its job. This step explains how, and how to swap the in-memory store for Redis so the cart also survives a server restart.
Sessions & state lifetime
Section titled “Sessions & state lifetime”A lifecycle: 'session' machine belongs to one visitor, identified by a session cookie Stator sets automatically. Between requests, the cart isn’t a live object sitting in memory — it’s a snapshot in the store, keyed by session. Each request restores the machines it needs, runs the event, and writes the touched ones back. (App-lifecycle machines like the catalog are the exception — they live in the process and aren’t per-session.)
This is why the runtime stays cheap: there’s no long-lived machine object per visitor, just snapshots that load and save around each request.
The Store interface
Section titled “The Store interface”A store is anything implementing four methods:
interface Store { get(sessionId: string, machineName: string): Promise<unknown | null> set(sessionId: string, machineName: string, snapshot: unknown, ttlSeconds: number): Promise<void> has(sessionId: string, machineName: string): Promise<boolean> deleteSession(sessionId: string): Promise<void>}Snapshots are opaque JSON — the store never needs to understand your machine, just persist its serialized form. TTL is per session, not per machine, so a whole cart expires together rather than losing line items mid-checkout.
In-memory default
Section titled “In-memory default”InMemoryStore (what we’ve used so far) keeps snapshots in a Map. It’s perfect for development and zero-config, with one catch: state lives only as long as the process. Restart the dev server and every cart is gone. You’ll see a warning to that effect on boot, which is your reminder that it’s not for production.
Swapping in Redis
Section titled “Swapping in Redis”Move to durable persistence by adding a stator.config.ts — no entry file to touch, and nothing in your machines or templates changes. Stator ships RedisStore and a CachedStore wrapper that fronts Redis with an in-memory cache to cut command counts:
import { defineConfig } from '@statorjs/stator/config'import { InMemoryStore, RedisStore, CachedStore, type Store } from '@statorjs/stator/server'
const store: Store = process.env.REDIS_URL ? new CachedStore(new RedisStore(process.env.REDIS_URL), { memoryTtlSeconds: 300, maxEntries: 10_000 }) : new InMemoryStore()
export default defineConfig({ persistence: { session: store }, sessions: { ttlSeconds: 86_400 }, // 24h idle window, refreshed on each cart action})Nothing in your machines or templates changes — the store is a pure infrastructure swap. Set REDIS_URL and carts now survive restarts and deploys.
What persists vs not
Section titled “What persists vs not”- Session machines (the cart) — persisted to the store, survive restarts on Redis.
- App machines (the catalog) — live in process memory; re-seeded on boot by default. A machine whose shared state must survive restarts opts in with
persist: trueand anAppStore— see App machines. For a seeded catalog, re-seeding is exactly right.
What you built · next
Section titled “What you built · next”A cart that persists across requests and — on Redis — across restarts, with no change to your application code. In step 8 we make a page update live as state changes elsewhere.