Skip to content

docs(cloudflare): fix env bindings access pattern for Nitro v3 (breaking change)#4064

Open
ruan-cat wants to merge 1 commit intonitrojs:mainfrom
ruan-cat:fix-doc/cloudflare-env-v3
Open

docs(cloudflare): fix env bindings access pattern for Nitro v3 (breaking change)#4064
ruan-cat wants to merge 1 commit intonitrojs:mainfrom
ruan-cat:fix-doc/cloudflare-env-v3

Conversation

@ruan-cat
Copy link

Note: This pull request was authored with AI assistance (Claude Sonnet 4.6). The documentation changes, code analysis, and technical explanations were generated and reviewed by an AI agent. The underlying issue was discovered and reported by a human developer.


Summary

This PR fixes outdated documentation for accessing Cloudflare Worker environment variables and bindings in Nitro v3. The existing docs still show the Nitro v2 pattern (event.context.cloudflare.env), which does not work in production with Nitro v3. This has been a source of confusion for developers migrating from v2 to v3.

Problem

The current documentation in docs/2.deploy/20.providers/cloudflare.md shows:

defineHandler(async (event) => {
  const { cloudflare } = event.context
  const stmt = await cloudflare.env.MY_D1.prepare('SELECT id FROM table')
  const { results } = await stmt.all()
})

This pattern (event.context.cloudflare.env) was correct in Nitro v2 but fails silently in Nitro v3 production deploymentsevent.context.cloudflare is undefined at runtime, so accessing .env on it throws or returns nothing.

Additionally, the migration guide (docs/1.docs/99.migration.md) has no mention of this breaking change, leaving developers without guidance when upgrading.

Root Cause

In Nitro v3, the underlying server layer switched from H3/unenv to srvx. As a result, the Cloudflare preset's production runtime entry now attaches the Cloudflare context to the request's runtime object rather than the event context.

The relevant code is in src/presets/cloudflare/runtime/_module-handler.ts:

// Lines 113-124
export function augmentReq(
  cfReq: Request | CF.Request,
  ctx: NonNullable<ServerRuntimeContext["cloudflare"]>
) {
  const req = cfReq as ServerRequest;
  req.runtime ??= { name: "cloudflare" };
  req.runtime.cloudflare = { ...req.runtime.cloudflare, ...ctx };  // <-- env lives here
  req.waitUntil = ctx.context?.waitUntil.bind(ctx.context);
}

The augmentReq() function is called in the production fetch handler:

async fetch(request, env, context) {
  (globalThis as any).__env__ = env;
  augmentReq(request as any, { env: env as any, context });  // attaches to req.runtime
  // ...
}

This means in Nitro v3, the correct path to access Cloudflare bindings is:

const { env } = event.req.runtime.cloudflare

Why does it seem to work in local dev?

The dev plugin (src/presets/cloudflare/runtime/plugin.dev.ts) still populates event.req.context.cloudflare for local development via the Wrangler proxy. This creates a confusing discrepancy: the old pattern works locally but fails in production.

Evidence

Debug Endpoint

A diagnostic API endpoint was written to probe all possible paths for Cloudflare env access in Nitro v3:

Source: server/api/debug-env.get.ts

Key probe from that file:

// @ts-ignore — event.req.runtime is the Nitro v3 Cloudflare runtime injection
const reqRuntime = (event.req as any)?.runtime;
if (reqRuntime !== undefined) {
  const cfRuntime = reqRuntime?.cloudflare;
  if (cfRuntime !== undefined) {
    const cfRuntimeEnv = cfRuntime?.env;
    // env keys are accessible here ✓
  }
}

Live Reproducible Demo

The endpoint is deployed to Cloudflare Workers using Nitro v3:

URL: https://01s-11.ruan-cat.com/api/debug-env

This live endpoint confirms that:

  • req.runtime.cloudflare.env contains the environment variables and bindings ✓
  • event.context.cloudflare is undefined in production ✗

Changes

This PR modifies documentation only — no source code changes.

docs/2.deploy/20.providers/cloudflare.md

  • Updated the "Direct access to Cloudflare bindings" section to show the correct Nitro v3 pattern (event.req.runtime.cloudflare.env)
  • Added a ::warning callout explaining the breaking change from v2

docs/1.docs/99.migration.md

  • Added a new "Cloudflare Bindings Access" section documenting the breaking change
  • Includes a diff migration example showing the old v2 pattern vs. the new v3 pattern
  • Includes a warning about the local dev vs. production discrepancy

Testing

The fix can be verified by deploying a Nitro v3 app to Cloudflare Workers and confirming that event.req.runtime.cloudflare.env returns the expected bindings. The live demo at https://01s-11.ruan-cat.com/api/debug-env serves as a reproducible test case.

Update cloudflare.md to show the correct v3 pattern (event.req.runtime.cloudflare.env)
and add a breaking change warning. Add migration section to 99.migration.md.
@ruan-cat ruan-cat requested a review from pi0 as a code owner February 28, 2026 14:29
@vercel
Copy link

vercel bot commented Feb 28, 2026

@ruan-cat is attempting to deploy a commit to the Nitro Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai
Copy link

coderabbitai bot commented Feb 28, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bfbb207 and 72c6b8f.

📒 Files selected for processing (2)
  • docs/1.docs/99.migration.md
  • docs/2.deploy/20.providers/cloudflare.md

📝 Walkthrough

Walkthrough

Documentation updates guide migration from event.context.cloudflare.env to event.req.runtime.cloudflare.env in Nitro v3, with deprecation warnings and revised code examples across migration and Cloudflare provider docs.

Changes

Cohort / File(s) Summary
Migration Documentation
docs/1.docs/99.migration.md
Adds "Cloudflare Bindings Access" migration section with code example and deprecation warning; minor text correction to Node.js recommendation.
Cloudflare Provider Documentation
docs/2.deploy/20.providers/cloudflare.md
Updates D1/Bindings example with new runtime access pattern and adds Nitro v3 breaking-change warning block.

Estimated code review effort

🎯 1 (Trivial) | ⏱️ ~3 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The pull request title follows conventional commits format with type 'docs' and clearly describes the main change: fixing Cloudflare env bindings access pattern for Nitro v3.
Description check ✅ Passed The pull request description is comprehensive and directly relates to the changeset, explaining the problem, root cause, and documentation changes made.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Tip

Try Coding Plans. Let us write the prompt for your AI agent so you can ship faster (with fewer bugs).
Share your feedback on Discord.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@ruan-cat
Copy link
Author

ruan-cat commented Feb 28, 2026

@pi0 Can you check and review it?

I was discovered within the nitro v3 version, with the aid of AI event. The event.req.runtime.cloudflare.env access to obtain cloudflare worker set environment variables. This helped me deploy a working vue project on cloudflare, successfully load environment variables from the neon database, and make valid http network requests.

I hope to merge into the main branch soon. This change is documentation only and adds more detail to the already patchy nitro v3 documentation.

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