Session Canvas/Docs

Deploy to Cloudflare Workers

Run Session Canvas on Cloudflare's edge via OpenNext — a working path with a few documented caveats.

Session Canvas runs on Cloudflare Workers through OpenNext. The repo ships the pieces: wrangler.jsonc, open-next.config.ts, and scripts/cloudflare-build.mjs, which wraps the build and papers over a few gaps between Next.js 16 and the current adapter.

Deploy

  1. Log in to Cloudflare

    pnpm wrangler login
  2. Create the R2 bucket that backs the ISR cache

    The public embed pages revalidate every 5 minutes through an R2-backed cache (R2 has to be enabled once for your account in the Cloudflare dashboard):

    pnpm wrangler r2 bucket create sessioncanvas-inc-cache
  3. Set the environment variables as Worker secrets

    pnpm wrangler secret put DATABASE_URL
    pnpm wrangler secret put DIRECT_URL
    pnpm wrangler secret put NEXT_PUBLIC_SUPABASE_URL
    pnpm wrangler secret put NEXT_PUBLIC_SUPABASE_ANON_KEY
    pnpm wrangler secret put SUPABASE_SERVICE_ROLE_KEY
    pnpm wrangler secret put RESEND_API_KEY
    pnpm wrangler secret put EMAIL_FROM
    pnpm wrangler secret put NEXT_PUBLIC_APP_URL
  4. Recommended: put Hyperdrive in front of Postgres

    Workers can’t reuse a TCP connection across requests, so without a pooler every request pays a fresh TLS handshake to your database:

    pnpm wrangler hyperdrive create sessioncanvas-hyperdrive \
      --connection-string="$DIRECT_URL"

    Add the binding to wrangler.jsonc:

    "hyperdrive": [{ "binding": "HYPERDRIVE", "id": "<id from the create command>" }]

    Then prefer the binding’s connection string in src/lib/db.ts:

    import { getCloudflareContext } from "@opennextjs/cloudflare";
    // inside createPrismaClient(), replace process.env.DATABASE_URL with:
    const connectionString = isWorkerd
      ? getCloudflareContext().env.HYPERDRIVE.connectionString
      : process.env.DATABASE_URL;
  5. Deploy

    pnpm deploy:cf     # build + wrangler deploy

    To try it locally first, pnpm preview:cf runs the real Worker build in workerd via wrangler dev, reading env vars from .dev.vars (copy your .env).

  6. Migrate, seed, and allow the Workers URL in Supabase

    Run pnpm db:migrate (and optionally pnpm db:seed) against the production database, and add your Workers URL to Supabase’s Authentication → URL Configuration → Redirect URLs — exactly as in the Vercel steps.

Known caveats

  • The OpenNext adapter (1.20.x) doesn’t support Next 16’s Node.js proxy.ts yet, so the Cloudflare build temporarily swaps in an equivalent edge-runtime middleware.ts shim (same logic and matchers, restored after the build).
  • The build script regenerates the Prisma client with runtime = "workerd" and patches Turbopack’s WASM loading for it — see the header comments in scripts/cloudflare-build.mjs.
  • In-Worker Postgres connections are deliberately not reused across requests (workerd forbids it) — use Hyperdrive for production traffic.
  • ISR revalidation runs through a Durable Object queue shipped inside the worker — nothing to create account-side.
  • Smoke-tested in workerd against a live Supabase database: home page, public CFP form, event pages, schedule embed (with ISR caching), admin shell, and the JSON/iCal feeds. Not yet exercised end-to-end on Workers: email sending, magic-link sign-in, file upload / ZIP export, and Airtable sync. Prefer Vercel if those must be battle-tested today.
  • Scheduled jobs (task reminders, Airtable sync) are registered as Vercel crons in vercel.json. On Workers, call the endpoints from a Cloudflare Cron Trigger or any external scheduler, sending Authorization: Bearer $CRON_SECRET.