Skip to content

Deploy to Cloudflare Workers (OpenNext + Hyperdrive) - #68

Open
alecf wants to merge 3 commits into
mainfrom
cloudflare-deploy
Open

alecf wants to merge 3 commits into
mainfrom
cloudflare-deploy

Conversation

@alecf

@alecf alecf commented Oct 6, 2026 •

Copy link
Copy Markdown
Owner

Sets the app up to run on Cloudflare Workers, with Postgres (Neon, for PostGIS later) behind Hyperdrive.

Why OpenNext and not vinext

npx vinext check reports next-auth as incompatible (it relies on Next API route internals). Moving to better-auth is its own project, so this uses the @opennextjs/cloudflare adapter, which supports next-auth.

Changes

  • wrangler.jsonc / open-next.config.ts: OpenNext Worker, Hyperdrive binding (HYPERDRIVE), Smart Placement (pages make several queries, so run near the DB), observability on. npm run preview / npm run deploy.
  • getDb() replaces the global db: Workers can't reuse a socket from another request, so on Cloudflare each request gets its own client (keyed on its ExecutionContext). Tests, scripts and plain next dev keep a single DATABASE_URL client. DATABASE_URL is now optional, since production uses the Hyperdrive binding.
  • next-auth is initialized lazily (NextAuth(createAuthConfig)), so the Drizzle adapter uses the request's client. trustHost: true, because Auth.js otherwise rejects any non-Vercel host.
  • Next pinned to ~16.3.8: on 16.4, every dynamic route 500s with Unexpected loadManifest(/.next/server/preview-props.json) ([BUG] Unexpected loadManifest(/.next/server/preview-props.json) call on Next.js 16.4 canary opennextjs/opennextjs-cloudflare#1355, fix in #1356). Dependabot ignores next >= 16.4 until then.
  • Runtime types come from @cloudflare/workers-types; the one binding we add (HYPERDRIVE) is declared in src/types/cloudflare-env.d.ts. Update it if wrangler.jsonc gains bindings.

Not done yet (needs accounts)

  • Neon project with PostGIS, migrations applied
  • wrangler hyperdrive create, replacing the placeholder id in wrangler.jsonc
  • Worker secrets: AUTH_SECRET, STRAVA_CLIENT_ID, STRAVA_CLIENT_SECRET
  • Strava app's callback domain set to the Workers hostname

Test plan

  • lint, typecheck, 142 tests, next build
  • opennextjs-cloudflare build, then wrangler dev against local Postgres: /, /recentmap, /api/auth/providers, /api/auth/session return 200; tRPC returns 401 without a session
  • Sign in with Strava on the deployed Worker

🤖 Generated with Claude Code

https://claude.ai/code/session_01LcV53ESAdFnYAbNewkMuUD

alecf and others added 2 commits October 6, 2026 16:50
- Add @opennextjs/cloudflare and wrangler config (Smart Placement,
  Hyperdrive binding, observability).
- Replace the global `db` with `getDb()`: one Hyperdrive-backed client per
  request on Workers (sockets can't cross requests), the DATABASE_URL client
  everywhere else. next-auth now builds its config per request.
- Set `trustHost` so Auth.js accepts the Worker's hostname.
- Pin Next to 16.3.x: the adapter 500s on 16.4 until
  opennextjs/opennextjs-cloudflare#1356 ships; Dependabot ignores 16.4+.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LcV53ESAdFnYAbNewkMuUD
`wrangler types` inlined ~16k lines of runtime types and, once the Worker had
been built, referenced `.open-next/worker` (absent in CI). The only binding we
add is HYPERDRIVE, so declare it by hand on top of the published types.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LcV53ESAdFnYAbNewkMuUD
@alecf
alecf force-pushed the cloudflare-deploy branch from b35b028 to 78c0c70 Compare October 6, 2026 23:50
- getDb() checks for the Workers runtime explicitly: Hyperdrive per request
  there (errors now surface instead of silently falling back), DATABASE_URL
  everywhere else, including `next dev`.
- Drop initOpenNextCloudflareForDev: nothing reads bindings under `next dev`
  any more, which also removes its startup race.
- Local Hyperdrive reads CLOUDFLARE_HYPERDRIVE_LOCAL_CONNECTION_STRING_HYPERDRIVE
  from .env rather than a hard-coded connection string.
- postgres.js: fetch_types: false (skip a round trip per request client).
- Typed global for the dev client instead of an `as` cast; fetch the client
  once in getStravaAccessToken.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LcV53ESAdFnYAbNewkMuUD
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant