• Blog
  • Docs
  • Pricing
  • We’re hiring!
Log inSign up
kamalnrf

kamalnrf

living-stories-self-registration

Registration for sessions at LSC Events
Public
Like
living-stories-self-registration
Home
Code
8
db
3
.vtignore
AGENTS.md
api.ts
deno.json
H
index.ts
seed-mcr-hrd.ts
seed.ts
Environment variables
Branches
1
Pull requests
Remixes
History
Val Town is a collaborative website to build and scale JavaScript apps.
Deploy APIs, crons, & store data – all from the browser, and deployed in milliseconds.
Sign up now
Code
/
AGENTS.md
Code
/
AGENTS.md
Search
2/14/2026
Viewing readonly version of main branch: v89
View latest version
AGENTS.md

Living Library — Self-Registration System

A self-service registration app for Living Library events in Hyderabad. Readers register with name + email, browse available "books" (people sharing their stories), and claim reading slots — all in real-time as 250+ people sign up simultaneously.

Architecture

┌─────────────────────────────────────────────────────────┐
│  Client (React)                              │
│  ┌──────────┐  ┌──────────────┐  ┌──────────────────┐  │
│  │ Register │→ │ Browse Books │→ │ Library Card View │  │
│  │ (name +  │  │ (polls every │  │ (reading history) │  │
│  │  email)  │  │  3 seconds)  │  │                   │  │
│  └──────────┘  └──────┬───────┘  └──────────────────┘  │
│                       │                                  │
│  localStorage: { id, name, email }                      │
└───────────────────────┼─────────────────────────────────┘
                        │ HTTP
┌───────────────────────┼─────────────────────────────────┐
│  Server (Hono)        │                                  │
│                       ▼                                  │
│  /api/events/active          GET  → active event         │
│  /api/events/:id/availability GET → sessions + slot counts│
│  /api/reader                 POST → find-or-create reader│
│  /api/register               POST → atomic slot booking  │
│  /api/register               DELETE → cancel booking     │
│  /api/readers/:id/card       GET  → library card data    │
│                                                          │
│  DB Layer: Drizzle ORM (sqlite-proxy → Val Town SQLite)  │
└──────────────────────────────────────────────────────────┘

Key Design Decisions

Polling over SSE/WebSocket

Val Town's serverless model keeps a Deno isolate alive per SSE connection. With 250 concurrent readers, that's 250 long-lived isolates — unsustainable. Instead, we poll every 3s. That's ~83 req/s of fast SQLite reads, which Val Town handles fine.

Atomic slot booking (race condition safety)

The critical path is two readers grabbing the last slot simultaneously. We solve this with a conditional INSERT:

INSERT INTO ll_registrations (reader_id, book_session_id, session_id) SELECT ?, ?, ? WHERE (SELECT COUNT(*) FROM ll_registrations WHERE book_session_id = ?) < (SELECT max_slots FROM ll_book_sessions WHERE id = ?)

If the slot was taken between the reader's last poll and their click, 0 rows are affected → friendly error.

Drizzle ORM with sqlite-proxy

Val Town provides SQLite via https://esm.town/v/std/sqlite (a Turso HTTP wrapper). We bridge it with drizzle-orm/sqlite-proxy which lets us plug in custom execute/batch callbacks. This gives us type-safe queries while staying on Val Town's native SQLite.

One book per session per reader

Enforced at the DB level with UNIQUE(reader_id, session_id) on ll_registrations. The API deletes any existing registration for a session before inserting a new one (in a transaction), so changing your pick is seamless.

Data Model

ll_events          → An event (e.g. "Living Library Hyderabad #6")
ll_sessions        → Time slots within an event (Session 1, 2, 3...)
ll_books           → "Books" (people) available at an event
ll_event_books     → Junction: event ↔ book with table_no (physical table assignment)
ll_book_sessions   → Junction: which books are in which sessions (with max_slots)
ll_readers         → Registered readers (name + email, unique on email)
ll_registrations   → Reader ↔ BookSession assignments

File Structure

├── CLAUDE.md              ← You are here
├── AGENTS.md              ← Val Town agent instructions
├── db/
│   ├── schema.ts          ← Drizzle table definitions (source of truth)
│   ├── client.ts          ← Drizzle instance via sqlite-proxy
│   └── queries.ts         ← All query functions
├── api.ts                 ← Hono API routes
├── index.ts               ← HTTP entry point (Hono app)
└── seed.ts                ← Script: populate sample data

Development

Seeding data

Edit seed.ts with your event/book/session details, then run it as a script in Val Town. Re-running clears and re-inserts.

Schema changes

  1. Update db/schema.ts (Drizzle table definitions)
  2. Update db/queries.ts if query logic changed
  3. The schema is applied via CREATE TABLE IF NOT EXISTS on cold start — for breaking changes, either drop tables manually or bump table names

Adding a new API endpoint

  1. Add the query function in db/queries.ts
  2. Add the route in api.ts
  3. Update the React app in views.ts if it needs to call the new endpoint

Trust model

We trust readers to provide accurate name/email. No auth, no verification. The UNIQUE(email) constraint on readers means returning with the same email resumes their session.

FeaturesVersion controlCode intelligenceCLIMCP
Use cases
TeamsAI agentsSlackGTM
DocsShowcaseTemplatesNewestTrendingAPI examplesNPM packages
PricingNewsletterBlogAboutCareers
We’re hiring!
Brandhi@val.townStatus
X (Twitter)
Discord community
GitHub discussions
YouTube channel
Bluesky
Open Source Pledge
Terms of usePrivacy policyAbuse contact
© 2026 Val Town, Inc.