Skip to content

ref(nuxt): Extract handler patching to extra plugin for Nitro v2/v3#19915

Open
s1gr1d wants to merge 4 commits intodevelopfrom
sig/nuxt-v5-handler
Open

ref(nuxt): Extract handler patching to extra plugin for Nitro v2/v3#19915
s1gr1d wants to merge 4 commits intodevelopfrom
sig/nuxt-v5-handler

Conversation

@s1gr1d
Copy link
Member

@s1gr1d s1gr1d commented Mar 20, 2026

h3App is called h3 in Nitro v3. Additionally, the type imports have changed.

This PR is just moving some code around (not changing any logic). Those are the made changes:

  • Installing nitro as a devDependency -> the new import in Nitro v3 (instead of nitropack)
  • Created extra plugin that just does the handler patching (to not have to duplicate everthing
  • Extracted the patching function as an utility function which accepts a generic type (as type imports differ in Nitro versions)

Currently, the handler.server.ts file (for Nuxt v5) is unused as we don't have a reliable way yet to tell whether Nitro v2 or v3 is running. In theory we could do something like this below but this only checks for Nuxt and not Nitro.

    // Checking for compatibilityVersion 5 in Nuxt, does not mean that Nitro v3 is installed for sure.
    if (nuxt.options.future.compatibilityVersion < 5) {
      addServerPlugin(moduleDirResolver.resolve('./runtime/plugins/handler.server'));
    } else {
      addServerPlugin(moduleDirResolver.resolve('./runtime/plugins/handler-legacy.server'));
    }

Closes #19913

@s1gr1d s1gr1d requested a review from logaretm March 20, 2026 13:02
@github-actions
Copy link
Contributor

github-actions bot commented Mar 20, 2026

Semver Impact of This PR

🟢 Patch (bug fixes)

📋 Changelog Preview

This is how your changes will appear in the changelog.
Entries from this PR are highlighted with a left border (blockquote style).


New Features ✨

Deps

  • Bump mongodb-memory-server-global from 10.1.4 to 11.0.1 by dependabot in #19888
  • Bump stacktrace-parser from 0.1.10 to 0.1.11 by dependabot in #19887

Bug Fixes 🐛

Core

  • Do not overwrite user provided conversation id in Vercel by nicohrubec in #19903
  • Return same value from startSpan as callback returns by s1gr1d in #19300

Deps

  • Bump next to 15.5.14 in nextjs-15 and nextjs-15-intl E2E test apps by chargome in #19917
  • Bump socket.io-parser to 4.2.6 to fix CVE-2026-33151 by chargome in #19880

Other

  • (cloudflare) Forward ctx argument to Workflow.do user callback by Lms24 in #19891
  • (craft) Add missing mainDocsUrl for @sentry/effect SDK by bc-sentry in #19860
  • (nestjs) Add node to nest metadata by chargome in #19875
  • (serverless) Add node to metadata by nicohrubec in #19878

Internal Changes 🔧

  • (astro) Re-enable server island tracing e2e test in Astro 6 by Lms24 in #19872
  • (ci) Fix "Gatbsy" typo in issue package label workflow by chargome in #19905
  • (lint) Resolve oxlint warnings by isaacs in #19893
  • (node-integration-tests) Remove unnecessary file-type dependency by Lms24 in #19824
  • (nuxt) Extract handler patching to extra plugin for Nitro v2/v3 by s1gr1d in #19915
  • (sveltekit) Replace recast + @babel/parser with acorn by roli-lpci in #19533
  • Add external contributor to CHANGELOG.md by javascript-sdk-gitflow in #19909

🤖 This preview updates automatically when you update the PR.

@s1gr1d s1gr1d force-pushed the sig/nuxt-v5-handler branch from 3844373 to 9c9528e Compare March 20, 2026 13:04
Comment on lines +83 to 84
addServerPlugin(moduleDirResolver.resolve('./runtime/plugins/handler-legacy.server'));
addServerPlugin(moduleDirResolver.resolve('./runtime/plugins/sentry.server'));
Copy link

Choose a reason for hiding this comment

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

Bug: The new handler-legacy.server.ts plugin and the existing sentry.server.ts plugin both patch nitroApp.h3App.handler, leading to the handler being wrapped twice.
Severity: MEDIUM

Suggested Fix

The refactoring appears to be incomplete. To avoid double patching, the patching logic within sentry.server.ts should be removed, as the intent was to centralize this functionality in the new handler-legacy.server.ts plugin.

Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent.
Verify if this is a real issue. If it is, propose a fix; if not, explain why it's not
valid.

Location: packages/nuxt/src/module.ts#L83-L84

Potential issue: The pull request introduces a new server plugin,
`handler-legacy.server.ts`, which patches the Nitro event handler
`nitroApp.h3App.handler`. However, the existing `sentry.server.ts` plugin, which also
performs the same patching, was not modified to remove its patching logic. Since both
plugins are registered unconditionally, the `handler-legacy.server.ts` plugin will run
first (due to alphabetical sorting), followed by `sentry.server.ts`, resulting in the
event handler being patched twice. This double-wrapping could lead to redundant
operations and potential unexpected side effects or performance degradation.

Did we get this right? 👍 / 👎 to inform future reviews.

"devDependencies": {
"@nuxt/module-builder": "^0.8.4",
"@nuxt/nitro-server": "^3.21.1",
"nitro": "^3.0.260311-beta",
Copy link
Member Author

Choose a reason for hiding this comment

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

I installed the beta version as the 3.0.0 version is using different types 😬

@s1gr1d s1gr1d requested review from Lms24 and chargome March 20, 2026 13:06
Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

Bugbot Autofix prepared a fix for the issue found in the latest run.

  • ✅ Fixed: Handler gets patched twice causing double Proxy wrapping
    • Removed the duplicate handler patching logic from sentry.server.ts so only the dedicated handler-legacy.server.ts plugin wraps nitroApp.h3App.handler once.

Create PR

Or push these changes by commenting:

@cursor push b1f2cae2f5
Preview (b1f2cae2f5)
diff --git a/packages/nuxt/src/runtime/plugins/sentry.server.ts b/packages/nuxt/src/runtime/plugins/sentry.server.ts
--- a/packages/nuxt/src/runtime/plugins/sentry.server.ts
+++ b/packages/nuxt/src/runtime/plugins/sentry.server.ts
@@ -1,11 +1,5 @@
-import {
-  debug,
-  flushIfServerless,
-  getDefaultIsolationScope,
-  getIsolationScope,
-  withIsolationScope,
-} from '@sentry/core';
-import type { EventHandler, H3Event } from 'h3';
+import { debug } from '@sentry/core';
+import type { H3Event } from 'h3';
 import type { NitroAppPlugin } from 'nitropack';
 import type { NuxtRenderHTMLContext } from 'nuxt/app';
 import { sentryCaptureErrorHook } from '../hooks/captureErrorHook';
@@ -13,8 +7,6 @@
 import { addSentryTracingMetaTags } from '../utils';
 
 export default (nitroApp => {
-  nitroApp.h3App.handler = patchEventHandler(nitroApp.h3App.handler);
-
   nitroApp.hooks.hook('beforeResponse', updateRouteBeforeResponse);
 
   nitroApp.hooks.hook('error', sentryCaptureErrorHook);
@@ -37,26 +29,3 @@
     }
   });
 }) satisfies NitroAppPlugin;
-
-function patchEventHandler(handler: EventHandler): EventHandler {
-  return new Proxy(handler, {
-    async apply(handlerTarget, handlerThisArg, handlerArgs: Parameters<EventHandler>) {
-      const isolationScope = getIsolationScope();
-      const newIsolationScope = isolationScope === getDefaultIsolationScope() ? isolationScope.clone() : isolationScope;
-
-      debug.log(
-        `Patched h3 event handler. ${
-          isolationScope === newIsolationScope ? 'Using existing' : 'Created new'
-        } isolation scope.`,
-      );
-
-      return withIsolationScope(newIsolationScope, async () => {
-        try {
-          return await handlerTarget.apply(handlerThisArg, handlerArgs);
-        } finally {
-          await flushIfServerless();
-        }
-      });
-    },
-  });
-}

This Bugbot Autofix run was free. To enable autofix for future PRs, go to the Cursor dashboard.

@github-actions
Copy link
Contributor

github-actions bot commented Mar 20, 2026

size-limit report 📦

⚠️ Warning: Base artifact is not the latest one, because the latest workflow run is not done yet. This may lead to incorrect results. Try to re-run all tests to get up to date results.

Path Size % Change Change
@sentry/browser 25.69 kB +0.2% +49 B 🔺
@sentry/browser - with treeshaking flags 24.17 kB +0.14% +33 B 🔺
@sentry/browser (incl. Tracing) 42.67 kB +0.13% +54 B 🔺
@sentry/browser (incl. Tracing, Profiling) 47.33 kB +0.12% +55 B 🔺
@sentry/browser (incl. Tracing, Replay) 81.48 kB +0.08% +57 B 🔺
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags 71.06 kB +0.1% +69 B 🔺
@sentry/browser (incl. Tracing, Replay with Canvas) 86.17 kB +0.06% +50 B 🔺
@sentry/browser (incl. Tracing, Replay, Feedback) 98.41 kB +0.04% +36 B 🔺
@sentry/browser (incl. Feedback) 42.48 kB +0.08% +30 B 🔺
@sentry/browser (incl. sendFeedback) 30.35 kB +0.15% +43 B 🔺
@sentry/browser (incl. FeedbackAsync) 35.4 kB +0.12% +39 B 🔺
@sentry/browser (incl. Metrics) 26.96 kB +0.15% +38 B 🔺
@sentry/browser (incl. Logs) 27.1 kB +0.12% +32 B 🔺
@sentry/browser (incl. Metrics & Logs) 27.78 kB +0.15% +39 B 🔺
@sentry/react 27.45 kB +0.22% +58 B 🔺
@sentry/react (incl. Tracing) 45.01 kB +0.14% +60 B 🔺
@sentry/vue 30.13 kB +0.16% +46 B 🔺
@sentry/vue (incl. Tracing) 44.52 kB +0.09% +39 B 🔺
@sentry/svelte 25.7 kB +0.16% +40 B 🔺
CDN Bundle 28.35 kB +0.27% +75 B 🔺
CDN Bundle (incl. Tracing) 43.57 kB +0.15% +62 B 🔺
CDN Bundle (incl. Logs, Metrics) 29.22 kB +0.27% +77 B 🔺
CDN Bundle (incl. Tracing, Logs, Metrics) 44.43 kB +0.17% +75 B 🔺
CDN Bundle (incl. Replay, Logs, Metrics) 68.29 kB +0.13% +85 B 🔺
CDN Bundle (incl. Tracing, Replay) 80.41 kB +0.1% +73 B 🔺
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) 81.31 kB +0.1% +76 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback) 85.97 kB +0.12% +103 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) 86.86 kB +0.1% +86 B 🔺
CDN Bundle - uncompressed 82.7 kB +0.1% +77 B 🔺
CDN Bundle (incl. Tracing) - uncompressed 128.62 kB +0.05% +64 B 🔺
CDN Bundle (incl. Logs, Metrics) - uncompressed 85.57 kB +0.1% +77 B 🔺
CDN Bundle (incl. Tracing, Logs, Metrics) - uncompressed 131.49 kB +0.05% +64 B 🔺
CDN Bundle (incl. Replay, Logs, Metrics) - uncompressed 209.22 kB +0.05% +102 B 🔺
CDN Bundle (incl. Tracing, Replay) - uncompressed 245.5 kB +0.04% +89 B 🔺
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) - uncompressed 248.35 kB +0.04% +89 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed 258.41 kB +0.04% +89 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) - uncompressed 261.26 kB +0.04% +89 B 🔺
@sentry/nextjs (client) 47.4 kB +0.08% +37 B 🔺
@sentry/sveltekit (client) 43.12 kB +0.12% +51 B 🔺
@sentry/node-core 56.42 kB +0.14% +74 B 🔺
@sentry/node 173.38 kB +0.13% +221 B 🔺
@sentry/node - without tracing 96.43 kB +0.1% +87 B 🔺
@sentry/aws-serverless 113.44 kB +0.09% +100 B 🔺

View base workflow run

@github-actions
Copy link
Contributor

github-actions bot commented Mar 20, 2026

node-overhead report 🧳

Note: This is a synthetic benchmark with a minimal express app and does not necessarily reflect the real-world performance impact in an application.

Scenario Requests/s % of Baseline Prev. Requests/s Change %
GET Baseline 9,155 - 11,240 -19%
GET With Sentry 1,657 18% 1,914 -13%
GET With Sentry (error only) 6,144 67% 7,552 -19%
POST Baseline 1,192 - 1,142 +4%
POST With Sentry 608 51% 590 +3%
POST With Sentry (error only) 1,045 88% 1,046 -0%
MYSQL Baseline 3,266 - 3,981 -18%
MYSQL With Sentry 467 14% 520 -10%
MYSQL With Sentry (error only) 2,661 81% 3,173 -16%

View base workflow run

Copy link
Member

@logaretm logaretm left a comment

Choose a reason for hiding this comment

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

LGTM, if we need to check Nitro's version we can probably do something like this and read it from the package json.

is there a possibility that Nuxt 4 uses Nitro v3?

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.

[Nuxt] Extract handler patching as extra plugin for Nuxt v5 (different nitroApp structure)

3 participants