Skip to main content
Version: 2.0 PartyKit

Data Persistence

Durable Object storage​

The GameSession Durable Object persists all state to built-in SQLite-backed storage. State survives DO eviction (Cloudflare may evict idle DOs after ~30 s of inactivity but restores from storage on next request).

DO storage keys​

KeyTypeValue
'state'SavedStateFull session snapshot (phase, wines, questions, currentIndices, timerMs)
'hostId'stringTANNIC-FALCON β€” used to authenticate rejoin_host
'participant:{rejoinToken}'object{ id, pseudonym, score, connected, answeredQuestions: string[] }
'response:{participantId}:{questionId}'object{ optionId, correct, points }

What SavedState contains​

interface SavedState {
phase: SessionPhase;
title: string;
wines: Wine[]; // full wine + question data
currentRoundIndex: number;
currentQuestionIndex: number;
timerSeconds: number; // configured at creation
remainingMs: number; // live timer state
createdAt: string;
}

Cloudflare KV​

KV is used for the host sessions index β€” a list of all sessions created by a given hostId.

KV namespace​

Name: SOMMELIER_HOSTS
Binding in partykit.json: HOSTS_KV

KV key format​

host:TANNIC-FALCON

KV value format​

[
{
"code": "4829",
"title": "Wine Night 1",
"createdAt": "2025-01-20T19:00:00.000Z",
"status": "ended",
"participantCount": 8,
"finalRankings": [
{ "pseudonym": "Alice", "score": 500, "rank": 1 }
]
}
]

The finalRankings field is written when the session ends (host:end).

What survives​

EventDO storageKV
DO eviction (idle)βœ… restored on next connectionβœ… unchanged
Page refresh (host)βœ… rejoin_host restores full stateβœ… unchanged
Page refresh (participant)βœ… rejoin_session + rejoinTokenβ€”
Server restart (local dev)❌ in-memory lost; storage persistsβœ… unchanged
partykit dev restart❌ local storage clearedβ€”

Note: In npx partykit dev mode, DO storage is in-memory only. Production Cloudflare Workers use real SQLite-backed DO storage.

localStorage (browser)​

KeyValueCleared when
sommelierArena:hostIdTANNIC-FALCONNever (user clears browser data)
sommelierArena:rejoin{ rejoinToken, code, pseudonym }session:ended received

In-Memory Data Model​

The following shows the runtime data shapes held in memory by the GameSession Durable Object (one instance per session code):

Game Session (one per room.id / session code):
β”œβ”€β”€ wines: Wine[] β€” list of wines with questions and options
β”œβ”€β”€ participants: Map β€” keyed by rejoinToken
β”‚ β”œβ”€β”€ id, socketId, pseudonym
β”‚ β”œβ”€β”€ score, connected
β”‚ └── answeredQuestions: Set
β”œβ”€β”€ phase: SessionPhase β€” waiting | question_open | question_paused | question_revealed | round_leaderboard | ended
β”œβ”€β”€ currentRound β€” index into wines[]
β”œβ”€β”€ currentQuestion β€” index into current wine's questions[]
β”œβ”€β”€ timerSeconds β€” configured timer duration (15–120s)
β”œβ”€β”€ timerRemainingMs β€” live countdown
β”œβ”€β”€ hostId β€” e.g. "TANNIC-FALCON"
└── sessionTitle β€” e.g. "Friday Wine Night"

Each Wine contains 5 questions (one per fixed category: color, country, grape_variety, vintage_year, wine_name). Each question carries 4 answer options (1 correct, 3 distractors). Clients only learn which option is correct when game:answer_revealed is emitted β€” options are sent without the correct flag during active play.