Skip to content

Local Development

This page covers the day-to-day development workflow for working on tripplan.ing locally using the Node runtime.

Setup

bash
npm install
npm run db:migrate:local
make dev

The dev server starts at http://localhost:5173 and serves both event sites and the platform UI.

Node runtime behavior

When running locally (no Cloudflare bindings), the app uses the Node runtime:

ComponentLocal implementation
Databasebetter-sqlite3 — SQLite file at apps/event-site/data/local.db
KV storeIn-memory Map — data lost on restart (sessions, OTPs)
Blob storageFilesystem at apps/event-site/data/objects/
Environment vars.dev.vars file in project root or process.env

Auto-migration and seeding

The Node runtime automatically:

  1. Creates the database file if missing
  2. Applies all pending migrations
  3. Seeds demo data (a sample event with content) on first run

This means npm run db:migrate:local is optional after the first setup — the dev server handles it. But running it explicitly is useful to verify migration files are correct.

Dev bypass

To skip authentication during development, set ENABLE_DEV_BYPASS=true in .dev.vars:

bash
# .dev.vars
ENABLE_DEV_BYPASS=true

When active, the dev bypass:

  • Automatically signs you in as dev@localhost
  • Grants admin access to all event admin routes
  • Grants super_admin access to platform routes
  • Skips OTP email sending entirely

WARNING

Dev bypass only activates when all four conditions are met:

  1. No Cloudflare bindings (platform.env is undefined)
  2. Hostname is localhost or 127.0.0.1
  3. NODE_ENV is not production
  4. ENABLE_DEV_BYPASS is explicitly 'true'

Hostname resolution

Locally, the server can't use subdomains for event resolution. Instead, resolveEventByHostname() has a fallback:

  • If no hostname match is found and allowSingleEventFallback is true (local dev), it returns the first available event
  • This means localhost:5173 automatically loads whatever event exists in the database

To test multiple events locally, you can add entries to /etc/hosts:

127.0.0.1 reunion.localhost
127.0.0.1 trip.localhost

Then access http://reunion.localhost:5173 and http://trip.localhost:5173 (add matching entries to platform_event_domains).

Environment variables

Create a .dev.vars file in the project root for local configuration:

bash
# Required for payments (optional for basic dev)
STRIPE_SECRET_KEY=sk_test_...
STRIPE_WEBHOOK_SECRET=whsec_...

# Required for emails (optional if using dev bypass)
MAILGUN_API_KEY=key-...
MAILGUN_DOMAIN=mg.example.com

# Platform config
PLATFORM_OPERATOR_EMAILS=dev@localhost
PLATFORM_DOMAIN_SUFFIX=localhost

# Dev convenience
ENABLE_DEV_BYPASS=true

Without Stripe/Mailgun credentials, payments and email features won't work but everything else functions normally.

Quality checks

Run these before pushing:

bash
npm run check           # TypeScript + Svelte type checking
npm test                # Unit and integration tests
npm run check:boundaries  # Monorepo import boundary validation

For schema changes:

bash
npm run db:generate     # Generate migration from schema.ts changes
npm run db:migrate:local  # Apply locally

For docs changes:

bash
npm run docs:check      # Link validation
npm run docs:build      # Full build test

Testing Stripe locally

To test payment flows with Stripe:

  1. Use a Stripe test mode API key in .dev.vars
  2. Install the Stripe CLI
  3. Forward webhooks to your local server:
bash
stripe listen --forward-to localhost:5173/api/stripe/webhook
  1. Use the webhook signing secret from the CLI output as STRIPE_WEBHOOK_SECRET

Debugging tips

  • Database inspection: The SQLite file is at apps/event-site/data/local.db. Use any SQLite client to inspect it directly.
  • KV is ephemeral: Sessions and OTPs are lost on server restart. If you get logged out, just refresh — dev bypass will recreate the session.
  • Blob storage: Uploaded files are in apps/event-site/data/objects/. You can browse them directly.
  • Hot reload: Vite hot-reloads client-side changes. Server-side changes require a page refresh.

Released under the MIT License.