Skip to content

feat(integrations): @workkit/opennext-cloudflare — Next.js + OpenNext on Workers integration #98

@beeeku

Description

@beeeku

Problem

Workkit ships first-class framework integrations under integrations/{astro,hono,remix}/ that wrap typed env access, binding helpers, and request-context plumbing. Astro is supported via @workkit/astro (Cloudflare Pages adapter — context.locals.runtime.env).

OpenNext on Cloudflare (@opennextjs/cloudflare) is becoming the default Next.js → Workers path, but consumers have no idiomatic way to get workkit's EnvAccessor / binding helpers inside a Next.js app:

  • Bindings live behind getCloudflareContext() from @opennextjs/cloudflare — async, sync-cached, and only valid inside the request scope.
  • Next.js Route Handlers, Server Components, Server Actions, and Middleware each have slightly different access patterns (request, async-local-storage, edge vs nodejs runtime).
  • @workkit/astro's defineEnv(astroContext) shape doesn't translate — there's no equivalent of context.locals.runtime to plumb through.

Proposed package

integrations/opennext-cloudflare/ → publishes as @workkit/opennext-cloudflare.

API mirrors @workkit/astro where it makes sense, adapted to Next's surface:

// In a Route Handler / Server Action / Server Component
import { defineEnv } from \"@workkit/opennext-cloudflare\";
import { z } from \"zod\";

const getEnv = defineEnv({
  DB: z.custom<D1Database>(),
  AI: z.custom<Ai>(),
  RESEND_API_KEY: z.string(),
});

export async function GET() {
  const env = await getEnv();           // wraps getCloudflareContext()
  const result = await env.DB.prepare(...).first();
  return Response.json(result);
}

Surface to design (this ticket is the umbrella — split into sub-issues during planning):

  1. defineEnv(schema) → async accessor that reads getCloudflareContext().env and validates via Standard Schema (same as @workkit/astro).
  2. getBindings() → untyped escape hatch for callers that just want env without a schema.
  3. getRequestCtx() → returns { env, ctx, cf } shape for parity with the Astro CloudflareRuntime type.
  4. Middleware adapter — Next.js middleware runs on the edge runtime; needs to handle the case where getCloudflareContext() is unavailable (build-time / dev) and degrade with a clear error message, not a generic "env is undefined."
  5. Docs — "Using workkit with Next.js + OpenNext on Workers" guide covering Route Handlers, Server Actions, Server Components, and Middleware.

Scope check / non-goals

  • Not a Next.js plugin / build tool. We don't fork or wrap @opennextjs/cloudflare — it stays a peer dependency.
  • Not a Pages adapter. Pages + Next.js is deprecated by Cloudflare; OpenNext is the path forward.
  • Not a re-export hub. Don't re-export @workkit/auth, @workkit/chat, etc. — consumers import those directly. This package is purely the env/bindings/context bridge.

Risks / open questions

  • getCloudflareContext() is async by default but also has a sync mode (getCloudflareContext({ async: false })). The accessor likely needs both shapes — getEnv() (async, safest) and getEnvSync() (sync, with the documented caveat that it must be called after the async version has run once in the request).
  • Edge runtime vs Node.js runtime: workkit packages assume workerd. Need to document that route handlers using workkit must opt into export const runtime = 'edge' or rely on OpenNext's default workerd execution.
  • Compatibility window: pin to a minimum @opennextjs/cloudflare version that has stable getCloudflareContext() semantics.

Acceptance

  • ADR for the integration shape (mirror or diverge from @workkit/astro?)
  • integrations/opennext-cloudflare/ scaffolded with same structure as integrations/astro/
  • defineEnv + getBindings + getRequestCtx implemented with tests against a mocked getCloudflareContext()
  • README with the four call-site recipes (Route Handler / Server Action / Server Component / Middleware)
  • Docs guide added under apps/docs/
  • Changeset

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions