[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"verticals":3,"article-nuxt-weekly-nuxt-server-import-boundary":44,"search-suggestions":450,"path-membership-nuxt-weekly-nuxt-server-import-boundary":491,"related-nuxt-weekly-nuxt-server-import-boundary":492,"code:ts:true:14srlhu":602,"code:plain:true:408xkr":603,"code:ts:true:c3mi2b":604,"code:ts:true:1sueaz7":605,"code:ts:true:1un6zgz":606,"comments-01a16761-c18b-70aa-93d5-4f4ab1a25a88":607},[4,20,32],{"id":5,"slug":6,"name":7,"tagline":8,"description":9,"accentFrom":10,"accentTo":11,"icon":12,"defaultLocale":13,"locales":14,"features":16,"position":19},"019fe637-3d33-714b-b57f-23e163ffca0c","dev","Web Development","Read it. Run it. Prove it.","A post a day on modern web development — most with an editable playground and a quiz that explains every answer. Free, no account needed.","violet-500","cyan-400","◇","en",[13,15],"fa",{"courses":17,"paths":17,"articles":17,"exams":18,"flashcards":18,"packages":17,"community":17,"certificates":17,"teams":17,"commerce":17},true,false,0,{"id":21,"slug":22,"name":23,"tagline":24,"description":25,"accentFrom":26,"accentTo":10,"icon":27,"defaultLocale":13,"locales":28,"features":30,"position":31},"019fe637-3dc2-754c-8657-0f175bfee7c6","lang","Languages","Learn a language the way you learn a codebase.","Grammar explained the way good documentation explains an API — one idea at a time, each with a quiz.","amber-400","⌘",[13,15,29],"es",{"courses":18,"paths":18,"articles":17,"exams":18,"flashcards":17,"packages":18,"community":17,"certificates":17,"teams":18,"commerce":18},2,{"id":33,"slug":34,"name":35,"tagline":36,"description":37,"accentFrom":38,"accentTo":39,"icon":40,"defaultLocale":13,"locales":41,"features":42,"position":43},"7b3c16f2-931d-410e-802e-e1fa4edab7de","soft","Soft Skills","The half of the job nobody wrote documentation for.","Weekly, on the parts of working life that decide more than your code does — first weeks, meetings, interviews, promotions, and the people around you. Written from what actually happens, and recorded as a podcast you can listen to on the walk.","emerald-400","teal-300","◉",[13],{"courses":18,"paths":18,"articles":17,"exams":18,"flashcards":18,"packages":18,"community":17,"certificates":18,"teams":18,"commerce":18},3,{"id":45,"slug":46,"title":47,"subtitle":48,"excerpt":49,"coverUrl":50,"locale":13,"readingMinutes":51,"publishedAt":52,"viewCount":53,"likeCount":19,"commentCount":19,"author":54,"vertical":59,"topic":60,"tags":63,"_count":74,"playground":76,"body":78,"bodyMd":399,"seo":400,"translationGroupId":402,"series":403,"podcastUrl":48,"verticalId":5,"thread":439,"assessments":441,"translations":446,"quiz":448},"01a16761-c18b-70aa-93d5-4f4ab1a25a88","nuxt-weekly-nuxt-server-import-boundary","Nuxt 4.6 nuxt\u002Fserver: Why Mixing h3 Imports Throws NUXT_E8012",null,"Nuxt 4.6 added a new nuxt\u002Fserver import source alongside h3 — mix the two in one handler and you hit NUXT_E8012. The real boundary, plus a cheat sheet.","\u002Fmedia\u002Fcovers\u002Fnuxt-weekly-nuxt-server-import-boundary.png",16,"2026-10-25T19:52:35.714Z",22,{"id":55,"name":56,"username":57,"avatarUrl":48,"headline":58},"019fe637-3c25-7088-9034-39c9f15dc3c8","Parsa Jiravand","parsa","Frontend engineer · building bestpractic",{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":61,"name":62},"nuxt","Nuxt",[64,65,68,71],{"slug":61,"name":62,"color":48},{"slug":66,"name":67,"color":48},"backend","Backend",{"slug":69,"name":70,"color":48},"javascript","Javascript",{"slug":72,"name":73,"color":48},"tutorial","Tutorial",{"assessments":75},1,{"slug":46,"title":77},"Nuxt 4.6 nuxt\u002Fserver — import-boundary playground",{"blocks":79,"version":75},[80,84,87,92,95,104,107,110,113,124,127,130,136,139,144,147,150,153,156,159,162,165,168,171,174,177,180,184,187,191,194,198,201,204,207,211,214,217,220,223,226,229,232,235,242,245,248,251,255,258,261,264,267,270,273,276,279,322,325,328,331,334,337,340,347,350,353,356,359,362,366,369,372,375,381,384,387,390,393],{"id":81,"html":82,"type":83},"b1","\u003Cp>A \u003Ccode>server\u002Fapi\u002Fprofile.get.ts\u003C\u002Fcode> handler that worked fine on Monday starts returning a \u003Ccode>NUXT_E8012\u003C\u002Fcode> error on Tuesday, and the only thing that changed is a teammate added one line — reading the signed-in user&#39;s session with a helper imported from \u003Ccode>nuxt\u002Fserver\u003C\u002Fcode>. Nothing else moved — not the route, not the \u003Ccode>defineEventHandler\u003C\u002Fcode> at the top of the file, not the dependency versions in any way you&#39;d notice without reading \u003Ccode>package.json\u003C\u002Fcode> closely. The error message is honest about what&#39;s wrong, but only if you already know \u003Ccode>server\u002F\u003C\u002Fcode> now has two event shapes floating around, and that this one new line just handed a helper the wrong one.\u003C\u002Fp>","paragraph",{"id":85,"html":86,"type":83},"b2","\u003Cp>This article is written against \u003Cstrong>Nuxt 4.6, verified at v4.6.2\u003C\u002Fstrong> (the current \u003Ccode>latest\u003C\u002Fcode> on npm as of this writing; \u003Ccode>nuxt\u002Fserver\u003C\u002Fcode> itself shipped in \u003Cstrong>4.6.0\u003C\u002Fstrong>, released October 5, 2026, per \u003Ca href=\"https:\u002F\u002Fgithub.com\u002Fnuxt\u002Fnuxt\u002Freleases\u002Ftag\u002Fv4.6.0\">the official GitHub release notes\u003C\u002Fa>). If you&#39;ve read \u003Ca href=\"https:\u002F\u002Fbestpractic.org\u002Fblog\u002Fnuxt-weekly-nitro-server-routes\">the server routes episode\u003C\u002Fa> in this series, you already know how \u003Ccode>server\u002Fapi\u003C\u002Fcode> and \u003Ccode>server\u002Fmiddleware\u003C\u002Fcode> route a request through Nitro — this one picks up what changed on top of that in 4.6: a second, explicit way to write the exact same kind of file, and the sharp edge where the two meet.\u003C\u002Fp>",{"id":88,"html":89,"text":90,"type":91,"level":31},"b3","What you&#39;ll learn","What you'll learn","heading",{"id":93,"html":94,"type":83},"b4","\u003Cp>By the end of this article you&#39;ll be able to:\u003C\u002Fp>",{"id":96,"type":97,"items":98,"ordered":18},"b5","list",[99,100,101,102,103],"Explain what \u003Ccode>nuxt\u002Fserver\u003C\u002Fcode> is, why Nuxt 4.6 added it, and what it means for Nuxt to be &quot;server-agnostic&quot;","Tell apart the two coexisting ways to write server code in Nuxt 4.6 — the default h3 auto-imports and the new explicit \u003Ccode>nuxt\u002Fserver\u003C\u002Fcode> imports","Read a \u003Ccode>NUXT_E8012\u003C\u002Fcode> error and fix the file it&#39;s pointing at","Spot the subtler bug: helpers with the \u003Cem>same name\u003C\u002Fem> that behave differently depending on which source they came from","Decide whether to migrate existing \u003Ccode>server\u002F\u003C\u002Fcode> code now, or leave it exactly as it is",{"id":105,"html":106,"text":106,"type":91,"level":31},"b6","Who this is for",{"id":108,"html":109,"type":83},"b7","\u003Cp>You&#39;ve written \u003Ccode>server\u002Fapi\u003C\u002Fcode> routes before — \u003Ccode>defineEventHandler\u003C\u002Fcode>, \u003Ccode>getQuery\u003C\u002Fcode>, \u003Ccode>readBody\u003C\u002Fcode> are already familiar. You don&#39;t need to have touched Nitro internals or h3 directly; this article treats the new \u003Ccode>nuxt\u002Fserver\u003C\u002Fcode> import source as its own subject, not an h3 tutorial.\u003C\u002Fp>",{"id":111,"html":112,"text":112,"type":91,"level":31},"b8","Table of contents",{"id":114,"type":97,"items":115,"ordered":18},"b9",[116,117,118,119,120,121,122,123],"\u003Ca href=\"#the-problem-one-new-import-one-confusing-error\">The problem: one new import, one confusing error\u003C\u002Fa>","\u003Ca href=\"#the-mental-model-two-server-apis-one-directory\">The mental model: two server APIs, one directory\u003C\u002Fa>","\u003Ca href=\"#walking-through-it-stage-by-stage\">Walking through it, stage by stage\u003C\u002Fa>","\u003Ca href=\"#edge-cases-and-gotchas\">Edge cases and gotchas\u003C\u002Fa>","\u003Ca href=\"#best-practices\">Best practices\u003C\u002Fa>","\u003Ca href=\"#faq\">FAQ\u003C\u002Fa>","\u003Ca href=\"#cheat-sheet\">Cheat sheet\u003C\u002Fa>","\u003Ca href=\"#key-takeaways\">Key takeaways\u003C\u002Fa>",{"id":125,"html":126,"text":126,"type":91,"level":31},"b10","The problem: one new import, one confusing error",{"id":128,"html":129,"type":83},"b11","\u003Cp>Here&#39;s a \u003Ccode>server\u002Fapi\u003C\u002Fcode> handler that looks like completely ordinary Nuxt 4 code, with one line added:\u003C\u002Fp>",{"id":131,"code":132,"type":133,"language":134,"highlight":135},"b12","\u002F\u002F server\u002Fapi\u002Fprofile.get.ts\nimport { useSession } from 'nuxt\u002Fserver'\n\nexport default defineEventHandler((event) => {\n  const id = getRouterParam(event, 'id')\n  const session = useSession(event, { password: process.env.NUXT_SESSION_PASSWORD! })\n  return { id, userId: session.data.userId }\n})","code","ts",[],{"id":137,"html":138,"type":83},"b13","\u003Cp>\u003Ccode>defineEventHandler\u003C\u002Fcode> and \u003Ccode>getRouterParam\u003C\u002Fcode> in this file are untouched — still the default auto-imports, exactly as they were last week. The only new thing is one call to \u003Ccode>useSession\u003C\u002Fcode>, imported explicitly from \u003Ccode>nuxt\u002Fserver\u003C\u002Fcode>, to read the signed-in user&#39;s id out of the session. Run the dev server and hit the route, and the response comes back a 500:\u003C\u002Fp>",{"id":140,"code":141,"type":133,"language":142,"highlight":143},"b14","[nuxt] NUXT_E8012: `useSession` from `nuxt\u002Fserver` was called with an h3 event. The\nhandler was defined with h3's `defineEventHandler` (auto-imported), which passes a\ndifferent event. Import `defineEventHandler` from `nuxt\u002Fserver` alongside the helpers.","plain",[],{"id":145,"html":146,"type":83},"b15","\u003Cp>The instinct is to assume a typo, a bad auto-import, or a stale \u003Ccode>.nuxt\u003C\u002Fcode> cache — none of which is true. \u003Ccode>getRouterParam\u003C\u002Fcode> and the rest of the file&#39;s identifiers were never touched, and this isn&#39;t a build failure either — the file compiles and the dev server starts just fine. The error only shows up once the route is actually \u003Cem>hit\u003C\u002Fem>, because it&#39;s thrown from inside \u003Ccode>useSession\u003C\u002Fcode> itself, the moment it notices the event it was handed doesn&#39;t look like the one \u003Ccode>nuxt\u002Fserver\u003C\u002Fcode> expects. That&#39;s the whole bug: a handler still built with h3&#39;s own \u003Ccode>defineEventHandler\u003C\u002Fcode> handed one of \u003Ccode>nuxt\u002Fserver\u003C\u002Fcode>&#39;s session helpers an event it can&#39;t work with — and it&#39;s also, it turns out, a deliberate guardrail rather than a bug at all.\u003C\u002Fp>",{"id":148,"html":149,"text":149,"type":91,"level":31},"b16","The mental model: two server APIs, one directory",{"id":151,"html":152,"type":83},"b17","\u003Cp>Before 4.6, \u003Ccode>server\u002F\u003C\u002Fcode> had exactly one API: a set of h3 and Nitro helpers (\u003Ccode>defineEventHandler\u003C\u002Fcode>, \u003Ccode>getQuery\u003C\u002Fcode>, \u003Ccode>readBody\u003C\u002Fcode>, \u003Ccode>getRouterParam\u003C\u002Fcode>, \u003Ccode>createError\u003C\u002Fcode>, and so on) that Nuxt auto-imports into every file under \u003Ccode>server\u002F\u003C\u002Fcode>. That hasn&#39;t gone away — \u003Ca href=\"https:\u002F\u002Fgithub.com\u002Fnuxt\u002Fnuxt\u002Freleases\u002Ftag\u002Fv4.6.0\">the release notes\u003C\u002Fa> are explicit that the auto-imported helpers &quot;are still h3&#39;s own helpers,&quot; unchanged, and \u003Cstrong>nothing in the 4.6 release requires you to touch them.\u003C\u002Fstrong>\u003C\u002Fp>",{"id":154,"html":155,"type":83},"b18","\u003Cp>What 4.6 adds is \u003Ccode>nuxt\u002Fserver\u003C\u002Fcode> — a second, \u003Cem>explicit\u003C\u002Fem> import source, described as &quot;a complement to \u003Ccode>nuxt\u002Fapp\u003C\u002Fcode>.&quot; Most of what it exports — \u003Ccode>getQuery\u003C\u002Fcode>, \u003Ccode>readBody\u003C\u002Fcode>, \u003Ccode>getRouterParam\u003C\u002Fcode>, and friends — are the exact same underlying h3\u002FNitro functions, re-exported under a new door, so importing them explicitly from \u003Ccode>nuxt\u002Fserver\u003C\u002Fcode> instead of relying on the auto-import changes nothing about how they behave. A few exports are genuinely different, though: \u003Ccode>nuxt\u002Fserver\u003C\u002Fcode> ships its \u003Cstrong>own\u003C\u002Fstrong> \u003Ccode>defineEventHandler\u003C\u002Fcode> (the one that builds the portable \u003Ccode>RequestEvent\u003C\u002Fcode>, with web-standard shape like \u003Ccode>event.req\u003C\u002Fcode> and \u003Ccode>event.res.headers\u003C\u002Fcode>), its \u003Cstrong>own\u003C\u002Fstrong> \u003Ccode>createError\u003C\u002Fcode> and \u003Ccode>sendRedirect\u003C\u002Fcode> with different contracts (more on those below), and its \u003Cstrong>own session API\u003C\u002Fstrong> — \u003Ccode>useSession\u003C\u002Fcode>, \u003Ccode>getSession\u003C\u002Fcode>, \u003Ccode>updateSession\u003C\u002Fcode>, \u003Ccode>clearSession\u003C\u002Fcode> — built specifically to read and write session data off that portable event.\u003C\u002Fp>",{"id":157,"html":158,"type":83},"b19","\u003Cp>That session API is the clearest place \u003Ccode>NUXT_E8012\u003C\u002Fcode> shows up. Each of those four helpers checks the event it&#39;s handed: if it looks like an h3 v1 event (no \u003Ccode>.req\u003C\u002Fcode>, just the legacy \u003Ccode>node\u003C\u002Fcode> request\u002Fresponse pair) rather than \u003Ccode>nuxt\u002Fserver\u003C\u002Fcode>&#39;s own event, it throws \u003Ccode>NUXT_E8012\u003C\u002Fcode>, naming the helper and telling you to import \u003Ccode>defineEventHandler\u003C\u002Fcode> from \u003Ccode>nuxt\u002Fserver\u003C\u002Fcode> too. That only happens if the handler around it is still built with the \u003Cstrong>auto-imported\u003C\u002Fstrong> \u003Ccode>defineEventHandler\u003C\u002Fcode> — the one that still produces an h3 v1 event. Reach for \u003Ccode>useSession\u003C\u002Fcode> (or one of its three siblings) inside a handler like that, and the event shape it&#39;s handed doesn&#39;t match what the helper needs. The session helpers aren&#39;t the only ones that check — the \u003Ca href=\"https:\u002F\u002Fgithub.com\u002Fnuxt\u002Fnuxt\u002Freleases\u002Ftag\u002Fv4.6.0\">release notes\u003C\u002Fa> describe the error more broadly, as something you&#39;ll hit from mixing the two import sources in general, so treat the session API as the headline example, not an exhaustive list.\u003C\u002Fp>",{"id":160,"html":161,"type":83},"b20","\u003Cp>The reason \u003Ccode>nuxt\u002Fserver\u003C\u002Fcode> exists at all is the headline of the release: Nuxt is moving toward being \u003Cstrong>server-agnostic\u003C\u002Fstrong> — not tied to Nitro as the only possible engine underneath \u003Ccode>server\u002F\u003C\u002Fcode>. A handler built end-to-end on \u003Ccode>nuxt\u002Fserver\u003C\u002Fcode>&#39;s portable event is meant to run unchanged whether the engine underneath it is Nitro, or the new experimental \u003Ccode>@nuxt\u002Fvite-server\u003C\u002Fcode>, or — per the release notes — Nitro v3 and h3 v2 once \u003Cstrong>Nuxt 5\u003C\u002Fstrong> ships. Nitro itself isn&#39;t going anywhere; the same notes call it &quot;still the right choice for almost everyone,&quot; including for 4.6.\u003C\u002Fp>",{"id":163,"html":164,"type":83},"b21","\u003Cp>\u003Cstrong>Key concept:\u003C\u002Fstrong> in this episode&#39;s example, \u003Ccode>NUXT_E8012\u003C\u002Fcode> comes from \u003Ccode>nuxt\u002Fserver\u003C\u002Fcode>&#39;s session helpers refusing to work with an event they can&#39;t read, because the handler around them never opted into the portable event that makes them work — \u003Ccode>getQuery\u003C\u002Fcode>, \u003Ccode>readBody\u003C\u002Fcode>, and \u003Ccode>getRouterParam\u003C\u002Fcode> don&#39;t care which \u003Ccode>defineEventHandler\u003C\u002Fcode> built the event, so pairing those with the auto-import doesn&#39;t trip it. The \u003Ca href=\"https:\u002F\u002Fgithub.com\u002Fnuxt\u002Fnuxt\u002Freleases\u002Ftag\u002Fv4.6.0\">release notes\u003C\u002Fa> frame the error more generally, as something mixing the two import sources can produce, so don&#39;t assume every other helper is exempt just because these three are.\u003C\u002Fp>",{"id":166,"html":167,"text":167,"type":91,"level":31},"b22","Walking through it, stage by stage",{"id":169,"html":170,"text":170,"type":91,"level":43},"b23","Stage 1 — the default path needs nothing from you",{"id":172,"html":173,"type":83},"b24","\u003Cp>If you&#39;ve never heard of \u003Ccode>nuxt\u002Fserver\u003C\u002Fcode>, your existing \u003Ccode>server\u002Fapi\u003C\u002Fcode>, \u003Ccode>server\u002Froutes\u003C\u002Fcode>, and \u003Ccode>server\u002Fmiddleware\u003C\u002Fcode> files are unaffected by 4.6. The auto-imported \u003Ccode>defineEventHandler\u003C\u002Fcode>, \u003Ccode>getQuery\u003C\u002Fcode>, \u003Ccode>readBody\u003C\u002Fcode>, \u003Ccode>getRouterParam\u003C\u002Fcode>, and \u003Ccode>createError\u003C\u002Fcode> are exactly the h3 helpers they always were. Upgrading to 4.6 changes nothing here — this is the whole point of the release notes&#39; line that &quot;nothing in this release \u003Cem>requires\u003C\u002Fem> a migration.&quot;\u003C\u002Fp>",{"id":175,"html":176,"text":176,"type":91,"level":43},"b25","Stage 2 — opting in, one file at a time",{"id":178,"html":179,"type":83},"b26","\u003Cp>To build a handler around the portable event — and unlock \u003Ccode>nuxt\u002Fserver\u003C\u002Fcode>&#39;s own \u003Ccode>defineEventHandler\u003C\u002Fcode>, \u003Ccode>createError\u003C\u002Fcode>, \u003Ccode>sendRedirect\u003C\u002Fcode>, and session helpers — you import explicitly, by name, from \u003Ccode>nuxt\u002Fserver\u003C\u002Fcode>:\u003C\u002Fp>",{"id":181,"code":182,"type":133,"language":134,"highlight":183},"b27","\u002F\u002F server\u002Fapi\u002Fhealth.get.ts — fully opted in\nimport { defineEventHandler, getQuery, useSession } from 'nuxt\u002Fserver'\n\nexport default defineEventHandler(async (event) => {\n  const { verbose } = getQuery(event)\n  const session = await useSession(event, { password: process.env.NUXT_SESSION_PASSWORD! })\n  return { ok: true, verbose: verbose === 'true', userId: session.data.userId }\n})",[],{"id":185,"html":186,"type":83},"b28","\u003Cp>Because \u003Ccode>defineEventHandler\u003C\u002Fcode> here is \u003Ccode>nuxt\u002Fserver\u003C\u002Fcode>&#39;s own version, the event it builds has \u003Ccode>.req\u003C\u002Fcode> on it — exactly the shape \u003Ccode>useSession\u003C\u002Fcode> checks for. \u003Ccode>getQuery\u003C\u002Fcode> behaves identically either way (it&#39;s the same h3 helper regardless of source), but the handler itself is opted all the way in, which is what the session API actually needs.\u003C\u002Fp>",{"id":188,"html":189,"text":190,"type":91,"level":43},"b29","Stage 3 — what actually trips \u003Ccode>NUXT_E8012\u003C\u002Fcode>","Stage 3 — what actually trips NUXTE8012",{"id":192,"html":193,"type":83},"b30","\u003Cp>This is the pairing from the mental model above, put to use: switch the handler&#39;s \u003Ccode>defineEventHandler\u003C\u002Fcode> to the \u003Ccode>nuxt\u002Fserver\u003C\u002Fcode> import too, and the session helper gets the portable event it&#39;s checking for.\u003C\u002Fp>",{"id":195,"code":196,"type":133,"language":134,"highlight":197},"b31","\u002F\u002F fixed — defineEventHandler now comes from nuxt\u002Fserver as well\nimport { defineEventHandler, getRouterParam, useSession } from 'nuxt\u002Fserver'\n\nexport default defineEventHandler(async (event) => {\n  const id = getRouterParam(event, 'id')\n  const session = await useSession(event, { password: process.env.NUXT_SESSION_PASSWORD! })\n  return { id, userId: session.data.userId }\n})",[],{"id":199,"html":200,"type":83},"b32","\u003Cp>Notice what \u003Cem>doesn&#39;t\u003C\u002Fem> need to change to clear the error: \u003Ccode>getRouterParam\u003C\u002Fcode> was never what threw \u003Ccode>NUXT_E8012\u003C\u002Fcode>, whether it came from the auto-import or from \u003Ccode>nuxt\u002Fserver\u003C\u002Fcode> — it doesn&#39;t check which \u003Ccode>defineEventHandler\u003C\u002Fcode> built the event the way the session helpers do. The one identifier that has to move to fix \u003Cem>this\u003C\u002Fem> error is \u003Ccode>defineEventHandler\u003C\u002Fcode> itself, because that&#39;s what decides which event shape every helper in the handler gets handed. \u003Ccode>getRouterParam\u003C\u002Fcode> itself behaves the same from either source, for what it&#39;s worth — see the next section for the helpers that actually do differ.\u003C\u002Fp>",{"id":202,"html":203,"text":203,"type":91,"level":43},"b33","Stage 4 — the quieter trap: same name, different contract",{"id":205,"html":206,"type":83},"b34","\u003Cp>A couple of helpers behave differently depending on which source they came from, and none of these trip \u003Ccode>NUXT_E8012\u003C\u002Fcode> either — that error is specific to the session API, not to these. \u003Ccode>nuxt\u002Fserver\u003C\u002Fcode>&#39;s \u003Ccode>sendRedirect\u003C\u002Fcode> still sets the response status and \u003Ccode>Location\u003C\u002Fcode> header as a side effect, exactly like the auto-imported one — but instead of ending the response there, it also \u003Cstrong>returns\u003C\u002Fstrong> an HTML \u003Ccode>&lt;meta http-equiv=&quot;refresh&quot;&gt;\u003C\u002Fcode> body as a fallback for a client that ignores the status code (inspecting the installed \u003Ccode>nuxt@4.6.2\u003C\u002Fcode> package directly: \u003Ccode>dist\u002Fserver\u002Findex.js\u003C\u002Fcode>&#39;s \u003Ccode>sendRedirect\u003C\u002Fcode> calls \u003Ccode>setResponseStatus(event, status)\u003C\u002Fcode> and \u003Ccode>event.res.headers.set(&#39;location&#39;, location)\u003C\u002Fcode> before returning that HTML string). So the redirect itself — the part every browser and \u003Ccode>fetch\u003C\u002Fcode> call actually relies on — works whether or not the handler returns that value; skipping the \u003Ccode>return\u003C\u002Fcode> only drops the fallback body, which matters solely to a user agent that ignores both the status and the header. Its \u003Ccode>createError\u003C\u002Fcode> takes \u003Cstrong>\u003Ccode>status\u003C\u002Fcode> and \u003Ccode>statusText\u003C\u002Fcode>\u003C\u002Fstrong>, where the h3-helper \u003Ccode>createError\u003C\u002Fcode> you already know takes \u003Ccode>statusCode\u003C\u002Fcode> and \u003Ccode>statusMessage\u003C\u002Fcode> — though \u003Ccode>nuxt\u002Fserver\u003C\u002Fcode>&#39;s \u003Ccode>createError\u003C\u002Fcode> still accepts \u003Ccode>statusCode\u003C\u002Fcode>\u002F\u003Ccode>statusMessage\u003C\u002Fcode> too, as deprecated aliases, so that direction keeps working. And response headers go through \u003Ccode>event.res.headers\u003C\u002Fcode>, a web-standard \u003Ccode>Headers\u003C\u002Fcode>-shaped object, rather than h3 v1&#39;s own header-setting helpers.\u003C\u002Fp>",{"id":208,"code":209,"type":133,"language":134,"highlight":210},"b35","\u002F\u002F h3-style (auto-imported) — sends the full response immediately as a side effect\nexport default defineEventHandler((event) => {\n  return sendRedirect(event, '\u002Flogin')\n})\n\n\u002F\u002F nuxt\u002Fserver-style — sets status + Location immediately too, then returns a fallback body\nimport { defineEventHandler, sendRedirect } from 'nuxt\u002Fserver'\n\nexport default defineEventHandler((event) => {\n  sendRedirect(event, '\u002Flogin')\n  \u002F\u002F no `return` here — the 302 and Location header are already set, so the\n  \u002F\u002F redirect still happens; only the \u003Cmeta refresh> fallback page is skipped\n})",[],{"id":212,"html":213,"type":83},"b36","\u003Cp>Neither example throws \u003Ccode>NUXT_E8012\u003C\u002Fcode> — that error only ever comes from the session helpers, never from \u003Ccode>sendRedirect\u003C\u002Fcode>. The real gotcha here is narrower than it looks: dropping the \u003Ccode>return\u003C\u002Fcode> doesn&#39;t break the redirect, it just means a browser gets an empty body alongside the 302 instead of the fallback HTML. Worth knowing, but not worth losing an afternoon over — the loud \u003Ccode>NUXT_E8012\u003C\u002Fcode> error from the session helpers is still the one that actually blocks a request.\u003C\u002Fp>",{"id":215,"html":216,"text":216,"type":91,"level":43},"b37","Stage 5 — deciding whether to opt in at all",{"id":218,"html":219,"type":83},"b38","\u003Cp>Nothing forces you to touch \u003Ccode>nuxt\u002Fserver\u003C\u002Fcode> today. The case for doing it anyway, per the release notes, is forward-looking: \u003Cstrong>Nuxt 5 will move to Nitro v3 and h3 v2\u003C\u002Fstrong>, and server code written against \u003Ccode>nuxt\u002Fserver\u003C\u002Fcode> on 4.6 &quot;runs unchanged there.&quot; Code left on the auto-imported h3 helpers isn&#39;t broken by this — it keeps working exactly as it did on Nuxt 4.5 — but it&#39;s the code that will need attention whenever that migration comes. If you&#39;re starting a new \u003Ccode>server\u002F\u003C\u002Fcode> file today and have no reason to care about portability, the auto-imports remain the simpler, fully-supported default. If you&#39;re building something you expect to still be maintaining past the next major, \u003Ccode>nuxt\u002Fserver\u003C\u002Fcode> is the version of &quot;write it once&quot; that this release is actually offering.\u003C\u002Fp>",{"id":221,"html":222,"text":222,"type":91,"level":31},"b39","Edge cases and gotchas",{"id":224,"html":225,"type":83},"b40","\u003Cp>\u003Cstrong>It&#39;s not a per-project switch — it&#39;s per call.\u003C\u002Fstrong> You can have some \u003Ccode>server\u002F\u003C\u002Fcode> files on the auto-imports and others fully on \u003Ccode>nuxt\u002Fserver\u003C\u002Fcode> in the same project; most helpers don&#39;t care which source built the event they&#39;re handed. Only a session-helper call cares, and only at the moment it runs — importing \u003Ccode>useSession\u003C\u002Fcode> and never calling it is harmless, since the check happens inside the helper itself, not from a static scan of the file&#39;s imports.\u003C\u002Fp>",{"id":227,"html":228,"type":83},"b41","\u003Cp>\u003Cstrong>\u003Ccode>@nuxt\u002Fvite-server\u003C\u002Fcode> is explicitly experimental.\u003C\u002Fstrong> The release notes introduce it alongside \u003Ccode>nuxt\u002Fserver\u003C\u002Fcode> as a second, pure-Vite server builder, but they&#39;re equally explicit that &quot;Nitro is still the right choice for almost everyone&quot; right now. Treat it as a preview of where server-agnostic Nuxt is headed, not a production alternative to reach for in 4.6.\u003C\u002Fp>",{"id":230,"html":231,"type":83},"b42","\u003Cp>\u003Cstrong>Server-side \u003Ccode>useState\u003C\u002Fcode>\u002F\u003Ccode>useRoute\u003C\u002Fcode> still don&#39;t belong in either server API.\u003C\u002Fstrong> Nothing about \u003Ccode>nuxt\u002Fserver\u003C\u002Fcode> changes the boundary \u003Ca href=\"https:\u002F\u002Fbestpractic.org\u002Fblog\u002Fnuxt-weekly-nitro-server-routes\">the server routes episode\u003C\u002Fa> in this series covers — both the h3-auto-import world and the \u003Ccode>nuxt\u002Fserver\u003C\u002Fcode> world are still the Nitro\u002Fh3 side of the project, with no Vue application instance for a composable to attach to.\u003C\u002Fp>",{"id":233,"html":234,"text":234,"type":91,"level":31},"b43","Best practices",{"id":236,"type":97,"items":237,"ordered":18},"b44",[238,239,240,241],"\u003Cstrong>If a handler calls \u003Ccode>useSession\u003C\u002Fcode>, \u003Ccode>getSession\u003C\u002Fcode>, \u003Ccode>updateSession\u003C\u002Fcode>, or \u003Ccode>clearSession\u003C\u002Fcode>, make sure that same handler&#39;s \u003Ccode>defineEventHandler\u003C\u002Fcode> is imported from \u003Ccode>nuxt\u002Fserver\u003C\u002Fcode> too.\u003C\u002Fstrong> That pairing is the one thing \u003Ccode>NUXT_E8012\u003C\u002Fcode> actually enforces — get it right and the rest of the file&#39;s imports (auto-imported or explicit) don&#39;t matter to this particular error.","\u003Cstrong>Don&#39;t migrate existing files just because 4.6 shipped.\u003C\u002Fstrong> The release notes are explicit that nothing requires it; migrate a file when you have an actual reason (adopting the session API, building for a new server builder, preparing early for Nuxt 5), not as busywork.","\u003Cstrong>When you do reach for \u003Ccode>nuxt\u002Fserver\u003C\u002Fcode>&#39;s own helpers, re-check the contract\u003C\u002Fstrong> — not just \u003Ccode>sendRedirect\u003C\u002Fcode> and \u003Ccode>createError\u003C\u002Fcode>, but anything that used to write directly to the response. The \u003Ca href=\"https:\u002F\u002Fnuxt.com\u002Fdocs\u002Fgetting-started\u002Fupgrade#moving-to-nuxtserver\">upgrade guide&#39;s comparison table\u003C\u002Fa> is the source of truth, not assumption-by-familiar-name.","\u003Cstrong>If a \u003Ccode>NUXT_E8012\u003C\u002Fcode> shows up in a file you didn&#39;t expect\u003C\u002Fstrong>, look for a call to \u003Ccode>useSession\u003C\u002Fcode>\u002F\u003Ccode>getSession\u003C\u002Fcode>\u002F\u003Ccode>updateSession\u003C\u002Fcode>\u002F\u003Ccode>clearSession\u003C\u002Fcode> and check what built the event around it — it&#39;s almost always a \u003Ccode>defineEventHandler\u003C\u002Fcode> that&#39;s still the h3 auto-import.",{"id":243,"html":244,"text":244,"type":91,"level":31},"b45","FAQ",{"id":246,"html":247,"text":247,"type":91,"level":43},"b46","Do I have to migrate my existing server\u002F code to nuxt\u002Fserver?",{"id":249,"html":250,"type":83},"b47","\u003Cp>No. The 4.6 release notes say directly that nothing in the release requires a migration — files using only the auto-imported h3 helpers keep working exactly as they did on 4.5.\u003C\u002Fp>",{"id":252,"html":253,"text":254,"type":91,"level":43},"b48","What exactly causes a NUXT_E8012 error?","What exactly causes a NUXTE8012 error?",{"id":256,"html":257,"type":83},"b49","\u003Cp>Calling one of \u003Ccode>nuxt\u002Fserver\u003C\u002Fcode>&#39;s session helpers — \u003Ccode>useSession\u003C\u002Fcode>, \u003Ccode>getSession\u003C\u002Fcode>, \u003Ccode>updateSession\u003C\u002Fcode>, or \u003Ccode>clearSession\u003C\u002Fcode> — inside a handler whose \u003Ccode>defineEventHandler\u003C\u002Fcode> is still the default auto-import. The helper gets handed an h3 v1 event instead of the portable one it needs, and throws at request time, naming itself in the error. It&#39;s not a build-time check and it&#39;s not about any other helper pairing — \u003Ccode>getQuery\u003C\u002Fcode>, \u003Ccode>readBody\u003C\u002Fcode>, and \u003Ccode>getRouterParam\u003C\u002Fcode> behave identically no matter which source you import them from.\u003C\u002Fp>",{"id":259,"html":260,"text":260,"type":91,"level":43},"b50","Is nuxt\u002Fserver replacing Nitro?",{"id":262,"html":263,"type":83},"b51","\u003Cp>No. Nitro remains the default engine underneath both import styles in 4.6, and the release notes call it &quot;still the right choice for almost everyone.&quot; \u003Ccode>nuxt\u002Fserver\u003C\u002Fcode> is a portable way to \u003Cem>write\u003C\u002Fem> handlers that happen to run on Nitro today and could run on a different server builder later — it isn&#39;t a replacement for Nitro itself.\u003C\u002Fp>",{"id":265,"html":266,"text":266,"type":91,"level":43},"b52","Do sendRedirect, createError, and getRouterParam behave the same regardless of where I import them from?",{"id":268,"html":269,"type":83},"b53","\u003Cp>Mostly, but not entirely. \u003Ccode>getRouterParam\u003C\u002Fcode>\u002F\u003Ccode>getRouterParams\u003C\u002Fcode> behave identically either way — both default to the raw, undecoded route segment, and both accept \u003Ccode>{ decode: true }\u003C\u002Fcode> to decode it. \u003Ccode>createError\u003C\u002Fcode> does differ: \u003Ccode>nuxt\u002Fserver\u003C\u002Fcode>&#39;s version takes \u003Ccode>status\u003C\u002Fcode>\u002F\u003Ccode>statusText\u003C\u002Fcode> (it still accepts \u003Ccode>statusCode\u003C\u002Fcode>\u002F\u003Ccode>statusMessage\u003C\u002Fcode> too, as deprecated aliases), while the auto-imported h3 helper only recognizes \u003Ccode>statusCode\u003C\u002Fcode>\u002F\u003Ccode>statusMessage\u003C\u002Fcode>. \u003Ccode>sendRedirect\u003C\u002Fcode> sets the status and \u003Ccode>Location\u003C\u002Fcode> header as a side effect either way, then additionally returns a \u003Ccode>&lt;meta refresh&gt;\u003C\u002Fcode> fallback body from the \u003Ccode>nuxt\u002Fserver\u003C\u002Fcode> version — skip the \u003Ccode>return\u003C\u002Fcode> and the redirect still happens, you just lose that fallback body. None of this is \u003Ccode>NUXT_E8012\u003C\u002Fcode>, which comes only from the session API checking the event shape it&#39;s handed.\u003C\u002Fp>",{"id":271,"html":272,"text":272,"type":91,"level":43},"b54","Should I use @nuxt\u002Fvite-server instead of Nitro?",{"id":274,"html":275,"type":83},"b55","\u003Cp>Not yet, for anything you&#39;re shipping. The release notes introduce it as experimental, in the same breath as recommending Nitro for almost everyone. It&#39;s worth knowing it exists as a sign of where Nuxt&#39;s server layer is heading, not as a current production choice.\u003C\u002Fp>",{"id":277,"html":278,"text":278,"type":91,"level":31},"b56","Cheat sheet",{"id":280,"head":281,"rows":284,"type":321},"b57",[282,283],"Situation","What to do",[285,288,291,294,297,300,303,306,309,312,315,318],[286,287],"New \u003Ccode>server\u002F\u003C\u002Fcode> file, no portability need","Leave it on the default auto-imports — no import needed",[289,290],"Want a handler portable beyond Nitro","Import everything server-related in that file from \u003Ccode>nuxt\u002Fserver\u003C\u002Fcode>",[292,293],"Calling \u003Ccode>getQuery\u003C\u002Fcode>\u002F\u003Ccode>readBody\u003C\u002Fcode> from either source","Behaves identically — they&#39;re the same h3 helper either way",[295,296],"Calling \u003Ccode>getRouterParam\u003C\u002Fcode>\u002F\u003Ccode>getRouterParams\u003C\u002Fcode> from either source","Behaves identically — both default to the \u003Cstrong>raw, undecoded\u003C\u002Fstrong> segment; pass \u003Ccode>{ decode: true }\u003C\u002Fcode> on either to decode it",[298,299],"Calling \u003Ccode>useSession\u003C\u002Fcode>\u002F\u003Ccode>getSession\u003C\u002Fcode>\u002F\u003Ccode>updateSession\u003C\u002Fcode>\u002F\u003Ccode>clearSession\u003C\u002Fcode>","Only works if that handler&#39;s \u003Ccode>defineEventHandler\u003C\u002Fcode> is also from \u003Ccode>nuxt\u002Fserver\u003C\u002Fcode>",[301,302],"\u003Ccode>NUXT_E8012\u003C\u002Fcode> in a file you didn&#39;t expect","Find the session-helper call and check whether its handler still uses the auto-imported \u003Ccode>defineEventHandler\u003C\u002Fcode>",[304,305],"Redirecting with the auto-imported \u003Ccode>sendRedirect\u003C\u002Fcode>","Just call it — it ends the response immediately as a side effect",[307,308],"Redirecting with \u003Ccode>nuxt\u002Fserver\u003C\u002Fcode>&#39;s \u003Ccode>sendRedirect\u003C\u002Fcode>","Sets status + \u003Ccode>Location\u003C\u002Fcode> immediately too; \u003Ccode>return sendRedirect(event, path)\u003C\u002Fcode> additionally sends a fallback body for clients that ignore the status",[310,311],"Throwing with the auto-imported \u003Ccode>createError\u003C\u002Fcode>","\u003Ccode>{ statusCode, statusMessage }\u003C\u002Fcode>",[313,314],"Throwing with \u003Ccode>nuxt\u002Fserver\u003C\u002Fcode>&#39;s \u003Ccode>createError\u003C\u002Fcode>","\u003Ccode>{ status, statusText }\u003C\u002Fcode>",[316,317],"Deciding whether to migrate at all","Only if you have a real reason — nothing requires it on 4.6",[319,320],"Trying \u003Ccode>@nuxt\u002Fvite-server\u003C\u002Fcode>","Experimental — fine to explore, not yet for production","table",{"id":323,"html":324,"type":83},"b58","\u003C!-- playground:start -->",{"id":326,"html":327,"text":327,"type":91,"level":31},"b59","🎮 Try it yourself",{"id":329,"html":330,"type":83},"b60","\u003Cp>\u003Cstrong>\u003Ca href=\"https:\u002F\u002Fbestpractic.org\u002Fblog\u002Fnuxt-weekly-nuxt-server-import-boundary\u002Fplayground\">▶️ Open the interactive playground →\u003C\u002Fa>\u003C\u002Fstrong>\u003C\u002Fp>",{"id":332,"html":333,"type":83},"b61","\u003Cp>\u003Cem>Runs right in your browser — poke at it and watch the concept react live.\u003C\u002Fem>\u003C\u002Fp>",{"id":335,"html":336,"type":83},"b62","\u003C!-- playground:end -->",{"id":338,"html":339,"text":339,"type":91,"level":31},"b63","Key takeaways",{"id":341,"type":97,"items":342,"ordered":18},"b64",[343,344,345,346],"Nuxt 4.6 added \u003Ccode>nuxt\u002Fserver\u003C\u002Fcode>, a new \u003Cstrong>explicit\u003C\u002Fstrong> import source for server code, alongside the \u003Cstrong>default auto-imported h3 helpers\u003C\u002Fstrong> that \u003Ccode>server\u002F\u003C\u002Fcode> has always had — most of what it exports is the same h3\u002FNitro functions under a new door, not a parallel implementation.","\u003Ccode>NUXT_E8012\u003C\u002Fcode> is narrower than &quot;don&#39;t mix imports&quot;: it fires specifically when \u003Ccode>nuxt\u002Fserver\u003C\u002Fcode>&#39;s session helpers (\u003Ccode>useSession\u003C\u002Fcode>, \u003Ccode>getSession\u003C\u002Fcode>, \u003Ccode>updateSession\u003C\u002Fcode>, \u003Ccode>clearSession\u003C\u002Fcode>) are called inside a handler whose \u003Ccode>defineEventHandler\u003C\u002Fcode> is still the auto-import — the fix is pairing that one import with \u003Ccode>nuxt\u002Fserver\u003C\u002Fcode> too.","\u003Ccode>createError\u003C\u002Fcode> shares a name across both sources but not a contract (\u003Ccode>status\u003C\u002Fcode>\u002F\u003Ccode>statusText\u003C\u002Fcode> vs. \u003Ccode>statusCode\u003C\u002Fcode>\u002F\u003Ccode>statusMessage\u003C\u002Fcode>), and \u003Ccode>sendRedirect\u003C\u002Fcode> sets its status and \u003Ccode>Location\u003C\u002Fcode> header the same way in both but only returns a fallback body from \u003Ccode>nuxt\u002Fserver\u003C\u002Fcode> — skipping that \u003Ccode>return\u003C\u002Fcode> loses the fallback body, not the redirect. \u003Ccode>getRouterParam\u003C\u002Fcode>\u002F\u003Ccode>getRouterParams\u003C\u002Fcode> behave identically from either source. None of these throw \u003Ccode>NUXT_E8012\u003C\u002Fcode>.","Migrating existing code to \u003Ccode>nuxt\u002Fserver\u003C\u002Fcode> is optional today, and mainly worth it if you want a handler to keep working unchanged once Nuxt 5 moves to Nitro v3 and h3 v2.",{"id":348,"html":349,"type":83},"b65","\u003C!-- quiz:start -->",{"id":351,"html":352,"text":352,"type":91,"level":31},"b66","🧠 Test yourself",{"id":354,"html":355,"type":83},"b67","\u003Cp>Think it clicked? \u003Cstrong>\u003Ca href=\"https:\u002F\u002Fbestpractic.org\u002Fblog\u002Fnuxt-weekly-nuxt-server-import-boundary\u002Fquiz\">Take the 8-question quiz →\u003C\u002Fa>\u003C\u002Fstrong>\u003C\u002Fp>",{"id":357,"html":358,"type":83},"b68","\u003Cp>\u003Cem>Instant feedback, a hint on every question, and an explanation for each answer — right or wrong.\u003C\u002Fem>\u003C\u002Fp>",{"id":360,"html":361,"type":83},"b69","\u003C!-- quiz:end -->",{"id":363,"html":364,"text":365,"type":91,"level":31},"b70","The teammate&#39;s one-line import, explained","The teammate's one-line import, explained",{"id":367,"html":368,"type":83},"b71","\u003Cp>That \u003Ccode>NUXT_E8012\u003C\u002Fcode> from the top of the article wasn&#39;t a cache problem or a typo — it was \u003Ccode>nuxt\u002Fserver\u003C\u002Fcode>&#39;s session API refusing to read a session off an event it doesn&#39;t recognize. Once you know the error belongs specifically to the session helpers, not to every helper in the file, it stops looking like a mystery and starts looking like a guardrail with a narrow, specific job — and the subtler case, the same-named helper that behaves differently with no error at all, is the one worth remembering long after a request stops failing.\u003C\u002Fp>",{"id":370,"html":371,"type":83},"b72","\u003C!-- related:start -->",{"id":373,"html":374,"text":374,"type":91,"level":31},"b73","📚 Read next",{"id":376,"type":97,"items":377,"ordered":18},"b74",[378,379,380],"\u003Ca href=\"https:\u002F\u002Fbestpractic.org\u002Fblog\u002Fnuxt-weekly-nitro-server-routes\">Nuxt Server Routes Explained: How Nitro Builds Your API\u003C\u002Fa>","\u003Ca href=\"https:\u002F\u002Fbestpractic.org\u002Fblog\u002Fnuxt-weekly-hydration-mismatch\">Nuxt Hydration Mismatch: Why It Happens and How to Fix It\u003C\u002Fa>","\u003Ca href=\"https:\u002F\u002Fbestpractic.org\u002Fblog\u002Fnuxt-weekly-ssr-streaming-route-rules\">Nuxt 4.5 SSR Streaming: The Route Rules That Disable It\u003C\u002Fa>",{"id":382,"html":383,"type":83},"b75","\u003C!-- related:end -->",{"id":385,"type":386},"b76","divider",{"id":388,"html":389,"type":83},"b77","\u003Cp>🚀 \u003Cstrong>Want more like this?\u003C\u002Fstrong> Every guide, playground, and quiz lives on \u003Cstrong>\u003Ca href=\"https:\u002F\u002Fbestpractic.org\u002F\">bestpractic.org\u003C\u002Fa>\u003C\u002Fstrong> — open it and \u003Cstrong>\u003Ca href=\"https:\u002F\u002Fbestpractic.org\u002F\">sign up free\u003C\u002Fa>\u003C\u002Fstrong> so the next one finds you.\u003C\u002Fp>",{"id":391,"html":392,"type":83},"b78","\u003Cp>\u003Cem>Thanks for reading! Let&#39;s stay connected:\u003C\u002Fem>\u003C\u002Fp>",{"id":394,"type":97,"items":395,"ordered":18},"b79",[396,397,398],"⭐ \u003Cstrong>GitHub\u003C\u002Fstrong> — follow me and star the projects: \u003Ca href=\"https:\u002F\u002Fgithub.com\u002Fparsajiravand\">github.com\u002Fparsajiravand\u003C\u002Fa>","💬 \u003Cstrong>Discord\u003C\u002Fstrong> — join the frontend best-practices community: \u003Ca href=\"https:\u002F\u002Fdiscord.gg\u002Fd9KRhuAwQ\">discord.gg\u002Fd9KRhuAwQ\u003C\u002Fa>","📸 \u003Cstrong>Instagram\u003C\u002Fstrong> — frontend best practices, daily: \u003Ca href=\"https:\u002F\u002Fwww.instagram.com\u002Fbestpractice___\u002F\">@bestpractice___\u003C\u002Fa>","A `server\u002Fapi\u002Fprofile.get.ts` handler that worked fine on Monday starts returning a `NUXT_E8012` error on Tuesday, and the only thing that changed is a teammate added one line — reading the signed-in user's session with a helper imported from `nuxt\u002Fserver`. Nothing else moved — not the route, not the `defineEventHandler` at the top of the file, not the dependency versions in any way you'd notice without reading `package.json` closely. The error message is honest about what's wrong, but only if you already know `server\u002F` now has two event shapes floating around, and that this one new line just handed a helper the wrong one.\n\nThis article is written against **Nuxt 4.6, verified at v4.6.2** (the current `latest` on npm as of this writing; `nuxt\u002Fserver` itself shipped in **4.6.0**, released October 5, 2026, per [the official GitHub release notes](https:\u002F\u002Fgithub.com\u002Fnuxt\u002Fnuxt\u002Freleases\u002Ftag\u002Fv4.6.0)). If you've read [the server routes episode](https:\u002F\u002Fbestpractic.org\u002Fblog\u002Fnuxt-weekly-nitro-server-routes) in this series, you already know how `server\u002Fapi` and `server\u002Fmiddleware` route a request through Nitro — this one picks up what changed on top of that in 4.6: a second, explicit way to write the exact same kind of file, and the sharp edge where the two meet.\n\n## What you'll learn\n\nBy the end of this article you'll be able to:\n\n- Explain what `nuxt\u002Fserver` is, why Nuxt 4.6 added it, and what it means for Nuxt to be \"server-agnostic\"\n- Tell apart the two coexisting ways to write server code in Nuxt 4.6 — the default h3 auto-imports and the new explicit `nuxt\u002Fserver` imports\n- Read a `NUXT_E8012` error and fix the file it's pointing at\n- Spot the subtler bug: helpers with the *same name* that behave differently depending on which source they came from\n- Decide whether to migrate existing `server\u002F` code now, or leave it exactly as it is\n\n## Who this is for\n\nYou've written `server\u002Fapi` routes before — `defineEventHandler`, `getQuery`, `readBody` are already familiar. You don't need to have touched Nitro internals or h3 directly; this article treats the new `nuxt\u002Fserver` import source as its own subject, not an h3 tutorial.\n\n## Table of contents\n\n- [The problem: one new import, one confusing error](#the-problem-one-new-import-one-confusing-error)\n- [The mental model: two server APIs, one directory](#the-mental-model-two-server-apis-one-directory)\n- [Walking through it, stage by stage](#walking-through-it-stage-by-stage)\n- [Edge cases and gotchas](#edge-cases-and-gotchas)\n- [Best practices](#best-practices)\n- [FAQ](#faq)\n- [Cheat sheet](#cheat-sheet)\n- [Key takeaways](#key-takeaways)\n\n## The problem: one new import, one confusing error\n\nHere's a `server\u002Fapi` handler that looks like completely ordinary Nuxt 4 code, with one line added:\n\n```ts\n\u002F\u002F server\u002Fapi\u002Fprofile.get.ts\nimport { useSession } from 'nuxt\u002Fserver'\n\nexport default defineEventHandler((event) => {\n  const id = getRouterParam(event, 'id')\n  const session = useSession(event, { password: process.env.NUXT_SESSION_PASSWORD! })\n  return { id, userId: session.data.userId }\n})\n```\n\n`defineEventHandler` and `getRouterParam` in this file are untouched — still the default auto-imports, exactly as they were last week. The only new thing is one call to `useSession`, imported explicitly from `nuxt\u002Fserver`, to read the signed-in user's id out of the session. Run the dev server and hit the route, and the response comes back a 500:\n\n```\n[nuxt] NUXT_E8012: `useSession` from `nuxt\u002Fserver` was called with an h3 event. The\nhandler was defined with h3's `defineEventHandler` (auto-imported), which passes a\ndifferent event. Import `defineEventHandler` from `nuxt\u002Fserver` alongside the helpers.\n```\n\nThe instinct is to assume a typo, a bad auto-import, or a stale `.nuxt` cache — none of which is true. `getRouterParam` and the rest of the file's identifiers were never touched, and this isn't a build failure either — the file compiles and the dev server starts just fine. The error only shows up once the route is actually *hit*, because it's thrown from inside `useSession` itself, the moment it notices the event it was handed doesn't look like the one `nuxt\u002Fserver` expects. That's the whole bug: a handler still built with h3's own `defineEventHandler` handed one of `nuxt\u002Fserver`'s session helpers an event it can't work with — and it's also, it turns out, a deliberate guardrail rather than a bug at all.\n\n## The mental model: two server APIs, one directory\n\nBefore 4.6, `server\u002F` had exactly one API: a set of h3 and Nitro helpers (`defineEventHandler`, `getQuery`, `readBody`, `getRouterParam`, `createError`, and so on) that Nuxt auto-imports into every file under `server\u002F`. That hasn't gone away — [the release notes](https:\u002F\u002Fgithub.com\u002Fnuxt\u002Fnuxt\u002Freleases\u002Ftag\u002Fv4.6.0) are explicit that the auto-imported helpers \"are still h3's own helpers,\" unchanged, and **nothing in the 4.6 release requires you to touch them.**\n\nWhat 4.6 adds is `nuxt\u002Fserver` — a second, *explicit* import source, described as \"a complement to `nuxt\u002Fapp`.\" Most of what it exports — `getQuery`, `readBody`, `getRouterParam`, and friends — are the exact same underlying h3\u002FNitro functions, re-exported under a new door, so importing them explicitly from `nuxt\u002Fserver` instead of relying on the auto-import changes nothing about how they behave. A few exports are genuinely different, though: `nuxt\u002Fserver` ships its **own** `defineEventHandler` (the one that builds the portable `RequestEvent`, with web-standard shape like `event.req` and `event.res.headers`), its **own** `createError` and `sendRedirect` with different contracts (more on those below), and its **own session API** — `useSession`, `getSession`, `updateSession`, `clearSession` — built specifically to read and write session data off that portable event.\n\nThat session API is the clearest place `NUXT_E8012` shows up. Each of those four helpers checks the event it's handed: if it looks like an h3 v1 event (no `.req`, just the legacy `node` request\u002Fresponse pair) rather than `nuxt\u002Fserver`'s own event, it throws `NUXT_E8012`, naming the helper and telling you to import `defineEventHandler` from `nuxt\u002Fserver` too. That only happens if the handler around it is still built with the **auto-imported** `defineEventHandler` — the one that still produces an h3 v1 event. Reach for `useSession` (or one of its three siblings) inside a handler like that, and the event shape it's handed doesn't match what the helper needs. The session helpers aren't the only ones that check — the [release notes](https:\u002F\u002Fgithub.com\u002Fnuxt\u002Fnuxt\u002Freleases\u002Ftag\u002Fv4.6.0) describe the error more broadly, as something you'll hit from mixing the two import sources in general, so treat the session API as the headline example, not an exhaustive list.\n\nThe reason `nuxt\u002Fserver` exists at all is the headline of the release: Nuxt is moving toward being **server-agnostic** — not tied to Nitro as the only possible engine underneath `server\u002F`. A handler built end-to-end on `nuxt\u002Fserver`'s portable event is meant to run unchanged whether the engine underneath it is Nitro, or the new experimental `@nuxt\u002Fvite-server`, or — per the release notes — Nitro v3 and h3 v2 once **Nuxt 5** ships. Nitro itself isn't going anywhere; the same notes call it \"still the right choice for almost everyone,\" including for 4.6.\n\n**Key concept:** in this episode's example, `NUXT_E8012` comes from `nuxt\u002Fserver`'s session helpers refusing to work with an event they can't read, because the handler around them never opted into the portable event that makes them work — `getQuery`, `readBody`, and `getRouterParam` don't care which `defineEventHandler` built the event, so pairing those with the auto-import doesn't trip it. The [release notes](https:\u002F\u002Fgithub.com\u002Fnuxt\u002Fnuxt\u002Freleases\u002Ftag\u002Fv4.6.0) frame the error more generally, as something mixing the two import sources can produce, so don't assume every other helper is exempt just because these three are.\n\n## Walking through it, stage by stage\n\n### Stage 1 — the default path needs nothing from you\n\nIf you've never heard of `nuxt\u002Fserver`, your existing `server\u002Fapi`, `server\u002Froutes`, and `server\u002Fmiddleware` files are unaffected by 4.6. The auto-imported `defineEventHandler`, `getQuery`, `readBody`, `getRouterParam`, and `createError` are exactly the h3 helpers they always were. Upgrading to 4.6 changes nothing here — this is the whole point of the release notes' line that \"nothing in this release *requires* a migration.\"\n\n### Stage 2 — opting in, one file at a time\n\nTo build a handler around the portable event — and unlock `nuxt\u002Fserver`'s own `defineEventHandler`, `createError`, `sendRedirect`, and session helpers — you import explicitly, by name, from `nuxt\u002Fserver`:\n\n```ts\n\u002F\u002F server\u002Fapi\u002Fhealth.get.ts — fully opted in\nimport { defineEventHandler, getQuery, useSession } from 'nuxt\u002Fserver'\n\nexport default defineEventHandler(async (event) => {\n  const { verbose } = getQuery(event)\n  const session = await useSession(event, { password: process.env.NUXT_SESSION_PASSWORD! })\n  return { ok: true, verbose: verbose === 'true', userId: session.data.userId }\n})\n```\n\nBecause `defineEventHandler` here is `nuxt\u002Fserver`'s own version, the event it builds has `.req` on it — exactly the shape `useSession` checks for. `getQuery` behaves identically either way (it's the same h3 helper regardless of source), but the handler itself is opted all the way in, which is what the session API actually needs.\n\n### Stage 3 — what actually trips `NUXT_E8012`\n\nThis is the pairing from the mental model above, put to use: switch the handler's `defineEventHandler` to the `nuxt\u002Fserver` import too, and the session helper gets the portable event it's checking for.\n\n```ts\n\u002F\u002F fixed — defineEventHandler now comes from nuxt\u002Fserver as well\nimport { defineEventHandler, getRouterParam, useSession } from 'nuxt\u002Fserver'\n\nexport default defineEventHandler(async (event) => {\n  const id = getRouterParam(event, 'id')\n  const session = await useSession(event, { password: process.env.NUXT_SESSION_PASSWORD! })\n  return { id, userId: session.data.userId }\n})\n```\n\nNotice what *doesn't* need to change to clear the error: `getRouterParam` was never what threw `NUXT_E8012`, whether it came from the auto-import or from `nuxt\u002Fserver` — it doesn't check which `defineEventHandler` built the event the way the session helpers do. The one identifier that has to move to fix *this* error is `defineEventHandler` itself, because that's what decides which event shape every helper in the handler gets handed. `getRouterParam` itself behaves the same from either source, for what it's worth — see the next section for the helpers that actually do differ.\n\n### Stage 4 — the quieter trap: same name, different contract\n\nA couple of helpers behave differently depending on which source they came from, and none of these trip `NUXT_E8012` either — that error is specific to the session API, not to these. `nuxt\u002Fserver`'s `sendRedirect` still sets the response status and `Location` header as a side effect, exactly like the auto-imported one — but instead of ending the response there, it also **returns** an HTML `\u003Cmeta http-equiv=\"refresh\">` body as a fallback for a client that ignores the status code (inspecting the installed `nuxt@4.6.2` package directly: `dist\u002Fserver\u002Findex.js`'s `sendRedirect` calls `setResponseStatus(event, status)` and `event.res.headers.set('location', location)` before returning that HTML string). So the redirect itself — the part every browser and `fetch` call actually relies on — works whether or not the handler returns that value; skipping the `return` only drops the fallback body, which matters solely to a user agent that ignores both the status and the header. Its `createError` takes **`status` and `statusText`**, where the h3-helper `createError` you already know takes `statusCode` and `statusMessage` — though `nuxt\u002Fserver`'s `createError` still accepts `statusCode`\u002F`statusMessage` too, as deprecated aliases, so that direction keeps working. And response headers go through `event.res.headers`, a web-standard `Headers`-shaped object, rather than h3 v1's own header-setting helpers.\n\n```ts\n\u002F\u002F h3-style (auto-imported) — sends the full response immediately as a side effect\nexport default defineEventHandler((event) => {\n  return sendRedirect(event, '\u002Flogin')\n})\n\n\u002F\u002F nuxt\u002Fserver-style — sets status + Location immediately too, then returns a fallback body\nimport { defineEventHandler, sendRedirect } from 'nuxt\u002Fserver'\n\nexport default defineEventHandler((event) => {\n  sendRedirect(event, '\u002Flogin')\n  \u002F\u002F no `return` here — the 302 and Location header are already set, so the\n  \u002F\u002F redirect still happens; only the \u003Cmeta refresh> fallback page is skipped\n})\n```\n\nNeither example throws `NUXT_E8012` — that error only ever comes from the session helpers, never from `sendRedirect`. The real gotcha here is narrower than it looks: dropping the `return` doesn't break the redirect, it just means a browser gets an empty body alongside the 302 instead of the fallback HTML. Worth knowing, but not worth losing an afternoon over — the loud `NUXT_E8012` error from the session helpers is still the one that actually blocks a request.\n\n### Stage 5 — deciding whether to opt in at all\n\nNothing forces you to touch `nuxt\u002Fserver` today. The case for doing it anyway, per the release notes, is forward-looking: **Nuxt 5 will move to Nitro v3 and h3 v2**, and server code written against `nuxt\u002Fserver` on 4.6 \"runs unchanged there.\" Code left on the auto-imported h3 helpers isn't broken by this — it keeps working exactly as it did on Nuxt 4.5 — but it's the code that will need attention whenever that migration comes. If you're starting a new `server\u002F` file today and have no reason to care about portability, the auto-imports remain the simpler, fully-supported default. If you're building something you expect to still be maintaining past the next major, `nuxt\u002Fserver` is the version of \"write it once\" that this release is actually offering.\n\n## Edge cases and gotchas\n\n**It's not a per-project switch — it's per call.** You can have some `server\u002F` files on the auto-imports and others fully on `nuxt\u002Fserver` in the same project; most helpers don't care which source built the event they're handed. Only a session-helper call cares, and only at the moment it runs — importing `useSession` and never calling it is harmless, since the check happens inside the helper itself, not from a static scan of the file's imports.\n\n**`@nuxt\u002Fvite-server` is explicitly experimental.** The release notes introduce it alongside `nuxt\u002Fserver` as a second, pure-Vite server builder, but they're equally explicit that \"Nitro is still the right choice for almost everyone\" right now. Treat it as a preview of where server-agnostic Nuxt is headed, not a production alternative to reach for in 4.6.\n\n**Server-side `useState`\u002F`useRoute` still don't belong in either server API.** Nothing about `nuxt\u002Fserver` changes the boundary [the server routes episode](https:\u002F\u002Fbestpractic.org\u002Fblog\u002Fnuxt-weekly-nitro-server-routes) in this series covers — both the h3-auto-import world and the `nuxt\u002Fserver` world are still the Nitro\u002Fh3 side of the project, with no Vue application instance for a composable to attach to.\n\n## Best practices\n\n- **If a handler calls `useSession`, `getSession`, `updateSession`, or `clearSession`, make sure that same handler's `defineEventHandler` is imported from `nuxt\u002Fserver` too.** That pairing is the one thing `NUXT_E8012` actually enforces — get it right and the rest of the file's imports (auto-imported or explicit) don't matter to this particular error.\n- **Don't migrate existing files just because 4.6 shipped.** The release notes are explicit that nothing requires it; migrate a file when you have an actual reason (adopting the session API, building for a new server builder, preparing early for Nuxt 5), not as busywork.\n- **When you do reach for `nuxt\u002Fserver`'s own helpers, re-check the contract** — not just `sendRedirect` and `createError`, but anything that used to write directly to the response. The [upgrade guide's comparison table](https:\u002F\u002Fnuxt.com\u002Fdocs\u002Fgetting-started\u002Fupgrade#moving-to-nuxtserver) is the source of truth, not assumption-by-familiar-name.\n- **If a `NUXT_E8012` shows up in a file you didn't expect**, look for a call to `useSession`\u002F`getSession`\u002F`updateSession`\u002F`clearSession` and check what built the event around it — it's almost always a `defineEventHandler` that's still the h3 auto-import.\n\n## FAQ\n\n### Do I have to migrate my existing server\u002F code to nuxt\u002Fserver?\n\nNo. The 4.6 release notes say directly that nothing in the release requires a migration — files using only the auto-imported h3 helpers keep working exactly as they did on 4.5.\n\n### What exactly causes a NUXT_E8012 error?\n\nCalling one of `nuxt\u002Fserver`'s session helpers — `useSession`, `getSession`, `updateSession`, or `clearSession` — inside a handler whose `defineEventHandler` is still the default auto-import. The helper gets handed an h3 v1 event instead of the portable one it needs, and throws at request time, naming itself in the error. It's not a build-time check and it's not about any other helper pairing — `getQuery`, `readBody`, and `getRouterParam` behave identically no matter which source you import them from.\n\n### Is nuxt\u002Fserver replacing Nitro?\n\nNo. Nitro remains the default engine underneath both import styles in 4.6, and the release notes call it \"still the right choice for almost everyone.\" `nuxt\u002Fserver` is a portable way to *write* handlers that happen to run on Nitro today and could run on a different server builder later — it isn't a replacement for Nitro itself.\n\n### Do sendRedirect, createError, and getRouterParam behave the same regardless of where I import them from?\n\nMostly, but not entirely. `getRouterParam`\u002F`getRouterParams` behave identically either way — both default to the raw, undecoded route segment, and both accept `{ decode: true }` to decode it. `createError` does differ: `nuxt\u002Fserver`'s version takes `status`\u002F`statusText` (it still accepts `statusCode`\u002F`statusMessage` too, as deprecated aliases), while the auto-imported h3 helper only recognizes `statusCode`\u002F`statusMessage`. `sendRedirect` sets the status and `Location` header as a side effect either way, then additionally returns a `\u003Cmeta refresh>` fallback body from the `nuxt\u002Fserver` version — skip the `return` and the redirect still happens, you just lose that fallback body. None of this is `NUXT_E8012`, which comes only from the session API checking the event shape it's handed.\n\n### Should I use @nuxt\u002Fvite-server instead of Nitro?\n\nNot yet, for anything you're shipping. The release notes introduce it as experimental, in the same breath as recommending Nitro for almost everyone. It's worth knowing it exists as a sign of where Nuxt's server layer is heading, not as a current production choice.\n\n## Cheat sheet\n\n| Situation | What to do |\n| --- | --- |\n| New `server\u002F` file, no portability need | Leave it on the default auto-imports — no import needed |\n| Want a handler portable beyond Nitro | Import everything server-related in that file from `nuxt\u002Fserver` |\n| Calling `getQuery`\u002F`readBody` from either source | Behaves identically — they're the same h3 helper either way |\n| Calling `getRouterParam`\u002F`getRouterParams` from either source | Behaves identically — both default to the **raw, undecoded** segment; pass `{ decode: true }` on either to decode it |\n| Calling `useSession`\u002F`getSession`\u002F`updateSession`\u002F`clearSession` | Only works if that handler's `defineEventHandler` is also from `nuxt\u002Fserver` |\n| `NUXT_E8012` in a file you didn't expect | Find the session-helper call and check whether its handler still uses the auto-imported `defineEventHandler` |\n| Redirecting with the auto-imported `sendRedirect` | Just call it — it ends the response immediately as a side effect |\n| Redirecting with `nuxt\u002Fserver`'s `sendRedirect` | Sets status + `Location` immediately too; `return sendRedirect(event, path)` additionally sends a fallback body for clients that ignore the status |\n| Throwing with the auto-imported `createError` | `{ statusCode, statusMessage }` |\n| Throwing with `nuxt\u002Fserver`'s `createError` | `{ status, statusText }` |\n| Deciding whether to migrate at all | Only if you have a real reason — nothing requires it on 4.6 |\n| Trying `@nuxt\u002Fvite-server` | Experimental — fine to explore, not yet for production |\n\n\u003C!-- playground:start -->\n\n## 🎮 Try it yourself\n\n**[▶️ Open the interactive playground →](https:\u002F\u002Fbestpractic.org\u002Fblog\u002Fnuxt-weekly-nuxt-server-import-boundary\u002Fplayground)**\n\n_Runs right in your browser — poke at it and watch the concept react live._\n\n\u003C!-- playground:end -->\n\n## Key takeaways\n\n- Nuxt 4.6 added `nuxt\u002Fserver`, a new **explicit** import source for server code, alongside the **default auto-imported h3 helpers** that `server\u002F` has always had — most of what it exports is the same h3\u002FNitro functions under a new door, not a parallel implementation.\n- `NUXT_E8012` is narrower than \"don't mix imports\": it fires specifically when `nuxt\u002Fserver`'s session helpers (`useSession`, `getSession`, `updateSession`, `clearSession`) are called inside a handler whose `defineEventHandler` is still the auto-import — the fix is pairing that one import with `nuxt\u002Fserver` too.\n- `createError` shares a name across both sources but not a contract (`status`\u002F`statusText` vs. `statusCode`\u002F`statusMessage`), and `sendRedirect` sets its status and `Location` header the same way in both but only returns a fallback body from `nuxt\u002Fserver` — skipping that `return` loses the fallback body, not the redirect. `getRouterParam`\u002F`getRouterParams` behave identically from either source. None of these throw `NUXT_E8012`.\n- Migrating existing code to `nuxt\u002Fserver` is optional today, and mainly worth it if you want a handler to keep working unchanged once Nuxt 5 moves to Nitro v3 and h3 v2.\n\n\u003C!-- quiz:start -->\n\n## 🧠 Test yourself\n\nThink it clicked? **[Take the 8-question quiz →](https:\u002F\u002Fbestpractic.org\u002Fblog\u002Fnuxt-weekly-nuxt-server-import-boundary\u002Fquiz)**\n\n_Instant feedback, a hint on every question, and an explanation for each answer — right or wrong._\n\n\u003C!-- quiz:end -->\n\n## The teammate's one-line import, explained\n\nThat `NUXT_E8012` from the top of the article wasn't a cache problem or a typo — it was `nuxt\u002Fserver`'s session API refusing to read a session off an event it doesn't recognize. Once you know the error belongs specifically to the session helpers, not to every helper in the file, it stops looking like a mystery and starts looking like a guardrail with a narrow, specific job — and the subtler case, the same-named helper that behaves differently with no error at all, is the one worth remembering long after a request stops failing.\n\n\u003C!-- related:start -->\n\n## 📚 Read next\n\n- [Nuxt Server Routes Explained: How Nitro Builds Your API](https:\u002F\u002Fbestpractic.org\u002Fblog\u002Fnuxt-weekly-nitro-server-routes)\n- [Nuxt Hydration Mismatch: Why It Happens and How to Fix It](https:\u002F\u002Fbestpractic.org\u002Fblog\u002Fnuxt-weekly-hydration-mismatch)\n- [Nuxt 4.5 SSR Streaming: The Route Rules That Disable It](https:\u002F\u002Fbestpractic.org\u002Fblog\u002Fnuxt-weekly-ssr-streaming-route-rules)\n\n\u003C!-- related:end -->\n\n---\n\n🚀 **Want more like this?** Every guide, playground, and quiz lives on **[bestpractic.org](https:\u002F\u002Fbestpractic.org\u002F)** — open it and **[sign up free](https:\u002F\u002Fbestpractic.org\u002F)** so the next one finds you.\n\n*Thanks for reading! Let's stay connected:*\n\n- ⭐ **GitHub** — follow me and star the projects: [github.com\u002Fparsajiravand](https:\u002F\u002Fgithub.com\u002Fparsajiravand)\n- 💬 **Discord** — join the frontend best-practices community: [discord.gg\u002Fd9KRhuAwQ](https:\u002F\u002Fdiscord.gg\u002Fd9KRhuAwQ)\n- 📸 **Instagram** — frontend best practices, daily: [@bestpractice___](https:\u002F\u002Fwww.instagram.com\u002Fbestpractice___\u002F)",{"title":47,"canonical":401,"description":49},"https:\u002F\u002Fbestpractic.org\u002Fblog\u002Fnuxt-weekly-nuxt-server-import-boundary","01a16761-c18b-70aa-93d5-511a499aaf71",{"name":404,"part":405,"total":405,"items":406},"Nuxt Deep Dive",8,[407,412,417,421,426,430,434,438],{"slug":408,"title":409,"publishedAt":410,"readingMinutes":411},"nuxt-weekly-cross-request-state-leak","Nuxt useState vs ref(): Why Server State Leaks Across Users","2026-08-30T11:37:11.335Z",14,{"slug":413,"title":414,"publishedAt":415,"readingMinutes":416},"nuxt-weekly-useasyncdata-keys-dedupe","useAsyncData Keys in Nuxt: Caching, Dedupe & the Sharing Bug","2026-09-13T11:26:28.014Z",15,{"slug":418,"title":419,"publishedAt":420,"readingMinutes":411},"nuxt-weekly-hydration-mismatch","Nuxt Hydration Mismatch: Why It Happens and How to Fix It","2026-09-20T16:26:05.191Z",{"slug":422,"title":423,"publishedAt":424,"readingMinutes":425},"nuxt-weekly-nitro-server-routes","Nuxt Server Routes Explained: How Nitro Builds Your API","2026-09-29T12:10:53.560Z",13,{"slug":427,"title":428,"publishedAt":429,"readingMinutes":411},"nuxt-weekly-ssr-streaming-route-rules","Nuxt 4.5 SSR Streaming: The Route Rules That Disable It","2026-10-04T12:03:48.007Z",{"slug":431,"title":432,"publishedAt":433,"readingMinutes":411},"nuxt-weekly-4-migration-breaking-changes","Nuxt 4 Migration: The Breaking Changes That Actually Bite","2026-10-11T12:26:26.492Z",{"slug":435,"title":436,"publishedAt":437,"readingMinutes":425},"nuxt-weekly-usesession-sealed-sessions","Nuxt 4.6 useSession: Server Sessions Without a Database","2026-10-18T10:15:34.613Z",{"slug":46,"title":47,"publishedAt":52,"readingMinutes":51},{"id":440,"locked":18},"01a16761-c1c4-729f-9f05-f2cc6bce2cf6",[442],{"id":443,"slug":46,"title":444,"_count":445},"01a16761-c1f7-76cc-893e-d7d53ea58b46","Nuxt 4.6: nuxt\u002Fserver & NUXT_E8012",{"questions":405},[447],{"locale":13,"slug":46},{"id":443,"slug":46,"title":444,"_count":449,"questionCount":405},{"questions":405},[451,455,457,461,463,467,471,474,478,481,484,487],{"slug":452,"name":453,"articles":454},"webdev","Webdev",152,{"slug":69,"name":70,"articles":456},126,{"slug":458,"name":459,"articles":460},"frontend","Frontend",88,{"slug":72,"name":73,"articles":462},59,{"slug":464,"name":465,"articles":466},"css","Css",45,{"slug":468,"name":469,"articles":470},"performance","Performance",25,{"slug":472,"name":473,"articles":53},"typescript","Typescript",{"slug":475,"name":476,"articles":477},"react","React",19,{"slug":479,"name":480,"articles":411},"browser","Browser",{"slug":482,"name":483,"articles":425},"programming","Programming",{"slug":485,"name":486,"articles":425},"node","Node",{"slug":488,"name":489,"articles":490},"accessibility","Accessibility",10,[],{"items":493,"meta":600},[494,506,523,538,555,570,585],{"id":45,"slug":46,"title":47,"subtitle":48,"excerpt":49,"coverUrl":50,"locale":13,"readingMinutes":51,"publishedAt":52,"viewCount":495,"likeCount":19,"commentCount":19,"author":496,"vertical":497,"topic":498,"tags":499,"_count":504,"playground":505,"hasQuiz":17,"hasPlayground":17},24,{"id":55,"name":56,"username":57,"avatarUrl":48,"headline":58},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":61,"name":62},[500,501,502,503],{"slug":61,"name":62,"color":48},{"slug":66,"name":67,"color":48},{"slug":69,"name":70,"color":48},{"slug":72,"name":73,"color":48},{"assessments":75},{"slug":46},{"id":507,"slug":435,"title":436,"subtitle":48,"excerpt":508,"coverUrl":509,"locale":13,"readingMinutes":425,"publishedAt":437,"viewCount":510,"likeCount":19,"commentCount":19,"author":511,"vertical":512,"topic":513,"tags":514,"_count":521,"playground":522,"hasQuiz":17,"hasPlayground":17},"01a143f6-4be7-7006-8c06-e84ad4e83026","Nuxt 4.6 ships useSession and runtimeConfig.appSecret — real, sealed-cookie sessions with no database or session store. How it works, plus a cheat sheet.","\u002Fmedia\u002Fcovers\u002Fnuxt-weekly-usesession-sealed-sessions.png",262,{"id":55,"name":56,"username":57,"avatarUrl":48,"headline":58},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":61,"name":62},[515,516,519,520],{"slug":61,"name":62,"color":48},{"slug":517,"name":518,"color":48},"security","Security",{"slug":69,"name":70,"color":48},{"slug":72,"name":73,"color":48},{"assessments":75},{"slug":435},{"id":524,"slug":431,"title":432,"subtitle":48,"excerpt":525,"coverUrl":526,"locale":13,"readingMinutes":411,"publishedAt":433,"viewCount":527,"likeCount":19,"commentCount":19,"author":528,"vertical":529,"topic":530,"tags":531,"_count":536,"playground":537,"hasQuiz":17,"hasPlayground":17},"01a11d02-8b28-774b-9f36-fb66bece8e50","Nuxt 3 reached end-of-life on July 31, 2026. Here's exactly what breaks moving to Nuxt 4 — directories, data fetching, TypeScript — plus a cheat sheet.","\u002Fmedia\u002Fcovers\u002Fnuxt-weekly-4-migration-breaking-changes.png",435,{"id":55,"name":56,"username":57,"avatarUrl":48,"headline":58},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":61,"name":62},[532,533,534,535],{"slug":61,"name":62,"color":48},{"slug":69,"name":70,"color":48},{"slug":72,"name":73,"color":48},{"slug":452,"name":453,"color":48},{"assessments":75},{"slug":431},{"id":539,"slug":427,"title":428,"subtitle":48,"excerpt":540,"coverUrl":541,"locale":13,"readingMinutes":411,"publishedAt":429,"viewCount":542,"likeCount":19,"commentCount":19,"author":543,"vertical":544,"topic":545,"tags":546,"_count":553,"playground":554,"hasQuiz":17,"hasPlayground":17},"01a0f8de-661d-724c-a8c3-3aef432860c4","Nuxt 4.5's experimental SSR streaming flushes HTML early, but cache, isr, swr, redirect, and ssr:false route rules silently fall back to buffering. Cheat sheet included.","\u002Fmedia\u002Fcovers\u002Fnuxt-weekly-ssr-streaming-route-rules.png",430,{"id":55,"name":56,"username":57,"avatarUrl":48,"headline":58},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":61,"name":62},[547,548,551,552],{"slug":61,"name":62,"color":48},{"slug":549,"name":550,"color":48},"ssr","SSR",{"slug":69,"name":70,"color":48},{"slug":72,"name":73,"color":48},{"assessments":75},{"slug":427},{"id":556,"slug":422,"title":423,"subtitle":48,"excerpt":557,"coverUrl":558,"locale":13,"readingMinutes":425,"publishedAt":424,"viewCount":559,"likeCount":19,"commentCount":19,"author":560,"vertical":561,"topic":562,"tags":563,"_count":568,"playground":569,"hasQuiz":17,"hasPlayground":17},"01a0ec72-faae-767b-a924-5ebb5891d4cd","Nuxt server routes turn server\u002Fapi into a real backend via Nitro and h3. Learn routing rules, defineEventHandler, middleware order, and the useState trap.","\u002Fmedia\u002Fcovers\u002Fnuxt-weekly-nitro-server-routes.png",563,{"id":55,"name":56,"username":57,"avatarUrl":48,"headline":58},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":61,"name":62},[564,565,566,567],{"slug":61,"name":62,"color":48},{"slug":66,"name":67,"color":48},{"slug":69,"name":70,"color":48},{"slug":72,"name":73,"color":48},{"assessments":75},{"slug":422},{"id":571,"slug":418,"title":419,"subtitle":48,"excerpt":572,"coverUrl":573,"locale":13,"readingMinutes":411,"publishedAt":420,"viewCount":574,"likeCount":19,"commentCount":19,"author":575,"vertical":576,"topic":577,"tags":578,"_count":583,"playground":584,"hasQuiz":17,"hasPlayground":17},"01a0b07e-f66a-736f-a8f4-3c87da225d8f","A Nuxt hydration mismatch happens when the server's HTML disagrees with the client's first render. Learn why, and the fixes that actually work.","\u002Fmedia\u002Fcovers\u002Fnuxt-weekly-hydration-mismatch.png",547,{"id":55,"name":56,"username":57,"avatarUrl":48,"headline":58},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":61,"name":62},[579,580,581,582],{"slug":61,"name":62,"color":48},{"slug":549,"name":550,"color":48},{"slug":69,"name":70,"color":48},{"slug":72,"name":73,"color":48},{"assessments":75},{"slug":418},{"id":586,"slug":413,"title":414,"subtitle":48,"excerpt":587,"coverUrl":588,"locale":13,"readingMinutes":416,"publishedAt":415,"viewCount":589,"likeCount":19,"commentCount":19,"author":590,"vertical":591,"topic":592,"tags":593,"_count":598,"playground":599,"hasQuiz":17,"hasPlayground":17},"01a08c59-464c-7039-acf3-b32a0b9ee7ce","Learn how Nuxt's useAsyncData and useFetch generate cache keys, how dedupe (cancel vs defer) really works, and why wrapper composables silently share data.","\u002Fmedia\u002Fcovers\u002Fnuxt-weekly-useasyncdata-keys-dedupe.png",521,{"id":55,"name":56,"username":57,"avatarUrl":48,"headline":58},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":61,"name":62},[594,595,596,597],{"slug":61,"name":62,"color":48},{"slug":549,"name":550,"color":48},{"slug":69,"name":70,"color":48},{"slug":72,"name":73,"color":48},{"assessments":75},{"slug":413},{"page":75,"perPage":601,"total":405,"totalPages":31},7,"\u003Cdiv class=\"shj shj-lang-ts shj-multiline\" data-lang=\"ts\">\u003Cdiv class=\"shj-scroll\">\u003Cdiv class=\"shj-numbers\">\u003Cdiv>1\u003C\u002Fdiv>\u003Cdiv>2\u003C\u002Fdiv>\u003Cdiv>3\u003C\u002Fdiv>\u003Cdiv>4\u003C\u002Fdiv>\u003Cdiv>5\u003C\u002Fdiv>\u003Cdiv>6\u003C\u002Fdiv>\u003Cdiv>7\u003C\u002Fdiv>\u003Cdiv>8\u003C\u002Fdiv>\u003C\u002Fdiv>\u003Cdiv class=\"shj-code\">\u003Cspan class=\"shj-cmnt\">\u002F\u002F server\u002Fapi\u002Fprofile.get.ts\n\u003C\u002Fspan>\u003Cspan class=\"shj-kwd\">import\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan> useSession \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">from\u003C\u002Fspan> \u003Cspan class=\"shj-str\">'nuxt\u002Fserver'\u003C\u002Fspan>\n\n\u003Cspan class=\"shj-kwd\">export\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">default\u003C\u002Fspan> \u003Cspan class=\"shj-func\">defineEventHandler\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>event\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">=&gt;\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n  \u003Cspan class=\"shj-kwd\">const\u003C\u002Fspan> id \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> \u003Cspan class=\"shj-func\">getRouterParam\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>event\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> \u003Cspan class=\"shj-str\">'id'\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\n  \u003Cspan class=\"shj-kwd\">const\u003C\u002Fspan> session \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> \u003Cspan class=\"shj-func\">useSession\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>event\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan> password\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> process\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>env\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-num\">NUXT_SESSION_PASSWORD\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">!\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\n  \u003Cspan class=\"shj-kwd\">return\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan> id\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> userId\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> session\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>data\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>userId \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\n\u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\u003C\u002Fdiv>\u003C\u002Fdiv>\u003C\u002Fdiv>","\u003Cdiv class=\"shj shj-lang-plain shj-multiline\" data-lang=\"plain\">\u003Cdiv class=\"shj-scroll\">\u003Cdiv class=\"shj-numbers\">\u003Cdiv>1\u003C\u002Fdiv>\u003Cdiv>2\u003C\u002Fdiv>\u003Cdiv>3\u003C\u002Fdiv>\u003C\u002Fdiv>\u003Cdiv class=\"shj-code\">[nuxt] NUXT_E8012: `useSession` from `nuxt\u002Fserver` was called with an h3 event. The\nhandler was defined with h3's `defineEventHandler` (auto-imported), which passes a\ndifferent event. Import `defineEventHandler` from `nuxt\u002Fserver` alongside the helpers.\u003C\u002Fdiv>\u003C\u002Fdiv>\u003C\u002Fdiv>","\u003Cdiv class=\"shj shj-lang-ts shj-multiline\" data-lang=\"ts\">\u003Cdiv class=\"shj-scroll\">\u003Cdiv class=\"shj-numbers\">\u003Cdiv>1\u003C\u002Fdiv>\u003Cdiv>2\u003C\u002Fdiv>\u003Cdiv>3\u003C\u002Fdiv>\u003Cdiv>4\u003C\u002Fdiv>\u003Cdiv>5\u003C\u002Fdiv>\u003Cdiv>6\u003C\u002Fdiv>\u003Cdiv>7\u003C\u002Fdiv>\u003Cdiv>8\u003C\u002Fdiv>\u003C\u002Fdiv>\u003Cdiv class=\"shj-code\">\u003Cspan class=\"shj-cmnt\">\u002F\u002F server\u002Fapi\u002Fhealth.get.ts — fully opted in\n\u003C\u002Fspan>\u003Cspan class=\"shj-kwd\">import\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan> defineEventHandler\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> getQuery\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> useSession \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">from\u003C\u002Fspan> \u003Cspan class=\"shj-str\">'nuxt\u002Fserver'\u003C\u002Fspan>\n\n\u003Cspan class=\"shj-kwd\">export\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">default\u003C\u002Fspan> \u003Cspan class=\"shj-func\">defineEventHandler\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-kwd\">async\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>event\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">=&gt;\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n  \u003Cspan class=\"shj-kwd\">const\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan> verbose \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan> \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> \u003Cspan class=\"shj-func\">getQuery\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>event\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\n  \u003Cspan class=\"shj-kwd\">const\u003C\u002Fspan> session \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">await\u003C\u002Fspan> \u003Cspan class=\"shj-func\">useSession\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>event\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan> password\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> process\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>env\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-num\">NUXT_SESSION_PASSWORD\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">!\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\n  \u003Cspan class=\"shj-kwd\">return\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan> ok\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-bool\">true\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> verbose\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> verbose \u003Cspan class=\"shj-oper\">===\u003C\u002Fspan> \u003Cspan class=\"shj-str\">'true'\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> userId\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> session\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>data\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>userId \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\n\u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\u003C\u002Fdiv>\u003C\u002Fdiv>\u003C\u002Fdiv>","\u003Cdiv class=\"shj shj-lang-ts shj-multiline\" data-lang=\"ts\">\u003Cdiv class=\"shj-scroll\">\u003Cdiv class=\"shj-numbers\">\u003Cdiv>1\u003C\u002Fdiv>\u003Cdiv>2\u003C\u002Fdiv>\u003Cdiv>3\u003C\u002Fdiv>\u003Cdiv>4\u003C\u002Fdiv>\u003Cdiv>5\u003C\u002Fdiv>\u003Cdiv>6\u003C\u002Fdiv>\u003Cdiv>7\u003C\u002Fdiv>\u003Cdiv>8\u003C\u002Fdiv>\u003C\u002Fdiv>\u003Cdiv class=\"shj-code\">\u003Cspan class=\"shj-cmnt\">\u002F\u002F fixed — defineEventHandler now comes from nuxt\u002Fserver as well\n\u003C\u002Fspan>\u003Cspan class=\"shj-kwd\">import\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan> defineEventHandler\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> getRouterParam\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> useSession \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">from\u003C\u002Fspan> \u003Cspan class=\"shj-str\">'nuxt\u002Fserver'\u003C\u002Fspan>\n\n\u003Cspan class=\"shj-kwd\">export\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">default\u003C\u002Fspan> \u003Cspan class=\"shj-func\">defineEventHandler\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-kwd\">async\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>event\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">=&gt;\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n  \u003Cspan class=\"shj-kwd\">const\u003C\u002Fspan> id \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> \u003Cspan class=\"shj-func\">getRouterParam\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>event\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> \u003Cspan class=\"shj-str\">'id'\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\n  \u003Cspan class=\"shj-kwd\">const\u003C\u002Fspan> session \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">await\u003C\u002Fspan> \u003Cspan class=\"shj-func\">useSession\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>event\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan> password\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> process\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>env\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-num\">NUXT_SESSION_PASSWORD\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">!\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\n  \u003Cspan class=\"shj-kwd\">return\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan> id\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> userId\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> session\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>data\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>userId \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\n\u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\u003C\u002Fdiv>\u003C\u002Fdiv>\u003C\u002Fdiv>","\u003Cdiv class=\"shj shj-lang-ts shj-multiline\" data-lang=\"ts\">\u003Cdiv class=\"shj-scroll\">\u003Cdiv class=\"shj-numbers\">\u003Cdiv>1\u003C\u002Fdiv>\u003Cdiv>2\u003C\u002Fdiv>\u003Cdiv>3\u003C\u002Fdiv>\u003Cdiv>4\u003C\u002Fdiv>\u003Cdiv>5\u003C\u002Fdiv>\u003Cdiv>6\u003C\u002Fdiv>\u003Cdiv>7\u003C\u002Fdiv>\u003Cdiv>8\u003C\u002Fdiv>\u003Cdiv>9\u003C\u002Fdiv>\u003Cdiv>10\u003C\u002Fdiv>\u003Cdiv>11\u003C\u002Fdiv>\u003Cdiv>12\u003C\u002Fdiv>\u003Cdiv>13\u003C\u002Fdiv>\u003C\u002Fdiv>\u003Cdiv class=\"shj-code\">\u003Cspan class=\"shj-cmnt\">\u002F\u002F h3-style (auto-imported) — sends the full response immediately as a side effect\n\u003C\u002Fspan>\u003Cspan class=\"shj-kwd\">export\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">default\u003C\u002Fspan> \u003Cspan class=\"shj-func\">defineEventHandler\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>event\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">=&gt;\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n  \u003Cspan class=\"shj-kwd\">return\u003C\u002Fspan> \u003Cspan class=\"shj-func\">sendRedirect\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>event\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> \u003Cspan class=\"shj-str\">'\u002Flogin'\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\n\u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\n\n\u003Cspan class=\"shj-cmnt\">\u002F\u002F nuxt\u002Fserver-style — sets status + Location immediately too, then returns a fallback body\n\u003C\u002Fspan>\u003Cspan class=\"shj-kwd\">import\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan> defineEventHandler\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> sendRedirect \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">from\u003C\u002Fspan> \u003Cspan class=\"shj-str\">'nuxt\u002Fserver'\u003C\u002Fspan>\n\n\u003Cspan class=\"shj-kwd\">export\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">default\u003C\u002Fspan> \u003Cspan class=\"shj-func\">defineEventHandler\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>event\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">=&gt;\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n  \u003Cspan class=\"shj-func\">sendRedirect\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>event\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> \u003Cspan class=\"shj-str\">'\u002Flogin'\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\n  \u003Cspan class=\"shj-cmnt\">\u002F\u002F no `return` here — the 302 and Location header are already set, so the\n\u003C\u002Fspan>  \u003Cspan class=\"shj-cmnt\">\u002F\u002F redirect still happens; only the &lt;meta refresh&gt; fallback page is skipped\n\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\u003C\u002Fdiv>\u003C\u002Fdiv>\u003C\u002Fdiv>",{"locked":18,"total":19,"comments":608},[]]