Skip to content

Add support for node middleware in cloudflare - #38

Open
conico974 wants to merge 3 commits into
conico/share-buildfrom
conico/node-middleware
Open

Add support for node middleware in cloudflare#38
conico974 wants to merge 3 commits into
conico/share-buildfrom
conico/node-middleware

Conversation

@conico974

@conico974 conico974 commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Add support for node middleware in cloudflare


Open in Devin Review

@pkg-pr-new

pkg-pr-new Bot commented Jul 15, 2026

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/opennextjs/adapters-api/@opennextjs/aws@65cdda7
npm i https://pkg.pr.new/opennextjs/adapters-api/@opennextjs/cloudflare@65cdda7
npm i https://pkg.pr.new/opennextjs/adapters-api/@opennextjs/core@65cdda7

commit: 65cdda7

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 1 potential issue.

View 1 additional finding in Devin Review.

Open in Devin Review

Comment on lines +95 to +100
...(config.middleware?.external
? [
openNextExternalMiddlewarePlugin(
path.join(buildOpts.openNextDistDir, "core/edgeFunctionHandler.js")
),
]

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 Middleware handler plugin added twice with conflicting target paths in Cloudflare builds

The external-middleware plugin is registered twice with different handler paths (openNextExternalMiddlewarePlugin(...) at packages/core/src/build/middleware/buildNodeMiddleware.ts:137-139 and again via additionalPlugins at packages/cloudflare/src/cli/adapter.ts:95-100), so the first plugin wins and the adapter's intended handler is silently ignored.

Impact: On Cloudflare, the middleware bundle may use the wrong handler module, potentially breaking middleware execution at runtime.

Duplicate esbuild plugin with conflicting resolve targets

The core function buildExternalNodeMiddleware unconditionally adds openNextExternalMiddlewarePlugin pointing to core/nodeMiddlewareHandler.js at packages/core/src/build/middleware/buildNodeMiddleware.ts:137-139. Then it spreads ...additionalPlugins at line 140, which for the Cloudflare adapter includes another openNextExternalMiddlewarePlugin pointing to core/edgeFunctionHandler.js (see packages/cloudflare/src/cli/adapter.ts:95-100).

Since buildExternalNodeMiddleware is only called when config.middleware?.external is true, the conditional guard in the Cloudflare adapter's middlewareBundle.additionalPlugins (config.middleware?.external ? [...] : []) always evaluates to true in this code path, meaning the plugin is always duplicated.

In esbuild, plugins are processed in registration order, so the core's plugin (registered first) resolves the import to nodeMiddlewareHandler.js, and the adapter's plugin (registered second) never gets a chance to redirect it to edgeFunctionHandler.js.

Prompt for agents
The Cloudflare adapter's middlewareBundle.additionalPlugins includes openNextExternalMiddlewarePlugin with edgeFunctionHandler.js, but the core's buildExternalNodeMiddleware (packages/core/src/build/middleware/buildNodeMiddleware.ts:137-139) already unconditionally adds the same plugin type with nodeMiddlewareHandler.js. This results in a duplicate plugin where the core's version takes precedence.

Two possible fixes:
1. Remove the openNextExternalMiddlewarePlugin from the Cloudflare adapter's middlewareBundle.additionalPlugins, since the core already adds it. But this only works if nodeMiddlewareHandler.js is the correct handler for Cloudflare middleware.
2. If the Cloudflare adapter needs edgeFunctionHandler.js instead, then the core's buildExternalNodeMiddleware should NOT hardcode the plugin, and instead let the adapter provide it via additionalPlugins. This would require removing lines 137-139 from buildNodeMiddleware.ts and ensuring all adapters that use external node middleware include the plugin in their middlewareBundle.additionalPlugins.

The right approach depends on whether Cloudflare middleware should use nodeMiddlewareHandler.js or edgeFunctionHandler.js.
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

@evanlong-me

Copy link
Copy Markdown

Independent validation (build + local runtime)

I hit this via Next 16 proxy.ts on Cloudflare (ERROR Node.js middleware is not currently supported) and tested this branch directly from source (conico/node-middleware @ e34ef09), because the published preview tarball is not installable outside the monorepo (@opennextjs/core@0.1.0 is not on npm; https://pkg.pr.new/@opennextjs/cloudflare@38 currently resolves to the old opennextjs-cloudflare PR #38 / v1.5.3).

Baseline (released @opennextjs/cloudflare@1.20.2)

Minimal Next 16.2.11 app with proxy.ts fails at adapter build:

ƒ Proxy (Middleware)
ERROR Node.js middleware is not currently supported. Consider switching to Edge Middleware.

This PR

Built the monorepo packages and ran the existing e2e app that already uses examples-cloudflare/e2e/app-router/proxy.ts:

# from repo root after pnpm install / package build
cd examples-cloudflare/e2e/app-router
node ../../../packages/cloudflare/dist/cli/index.js build
node ../../../packages/cloudflare/dist/cli/index.js preview -- --port 8799

Build: success. Output includes external middleware bundling:

Bundling middleware function...
Copying adapter files for external middleware...
Middleware created
ƒ Proxy (Middleware)
Worker saved in `.open-next/worker.js`

Worker entry wires middleware:

import { handler as middlewareHandler } from "./middleware/handler.mjs";

Runtime checks against local preview (http://127.0.0.1:8799):

Route Result
HEAD / 200, middleware cookies (from=middleware, with=love) + response-header: response-header
HEAD /redirect 307/redirect-destination, Set-Cookie: test=success
HEAD /cookies 200, Set-Cookie: foo=bar
GET /api/middleware 200 {"hello":"middleware"}
HEAD /rewrite 500 SyntaxError: Unexpected end of JSON input (may be HEAD-specific; flagging)

So the core Node/proxy.ts path works end-to-end for cookies / redirect / direct middleware response. This unblocks Next 16 apps that can no longer opt proxy.ts back to Edge.

Merge blockers I can see

  1. PR is stacked on conico/share-build (depends on Make a default overridable adapter in core #35), not main.
  2. GitHub currently reports mergeable_state: dirty vs conico/share-build — needs rebase.
  3. Preview install story is broken for external testers (@opennextjs/core not published; short pkg.pr.new URL points at the wrong repo). Publishing paired @opennextjs/cloudflare + @opennextjs/core previews (or documenting monorepo-only testing) would get more validation.

Happy to re-test after rebase, or run a fuller Playwright pass if useful. Real production apps (including mine) are still stuck on the middleware.ts deprecation workaround until this lands.

@conico974
conico974 force-pushed the conico/node-middleware branch from e34ef09 to 65cdda7 Compare August 2, 2026 09:54
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.

2 participants