[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"verticals":3,"article-nuxt-weekly-usesession-sealed-sessions":44,"search-suggestions":452,"path-membership-nuxt-weekly-usesession-sealed-sessions":496,"related-nuxt-weekly-usesession-sealed-sessions":497,"code:ts:true:mrv13h":606,"code:ts:true:q3im02":607,"code:bash:true:150lh0i":608,"code:bash:true:1d1d9k3":609,"code:ts:true:1u7g1cd":610,"code:ts:true:rjkprb":611,"code:ts:true:xxkb1e":612,"code:ts:true:uekcan":613,"code:ts:true:57pcip":614,"comments-01a143f6-4be7-7006-8c06-e84ad4e83026":615},[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":405,"seo":406,"translationGroupId":408,"series":409,"podcastUrl":48,"verticalId":5,"thread":440,"assessments":442,"translations":448,"quiz":450},"01a143f6-4be7-7006-8c06-e84ad4e83026","nuxt-weekly-usesession-sealed-sessions","Nuxt 4.6 useSession: Server Sessions Without a Database",null,"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",13,"2026-10-18T10:15:34.613Z",35,{"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},"security","Security",{"slug":69,"name":70,"color":48},"javascript","Javascript",{"slug":72,"name":73,"color":48},"tutorial","Tutorial",{"assessments":75},1,{"slug":46,"title":77},"Nuxt useSession — seal, tamper &amp; wrong-secret playground",{"blocks":79,"version":75},[80,84,87,92,95,104,107,110,113,116,126,129,132,138,142,145,148,151,154,157,160,167,170,174,177,180,183,188,192,195,198,202,205,208,211,215,219,222,226,229,233,236,239,247,250,269,272,275,279,282,286,289,293,296,299,302,305,308,311,315,346,349,357,360,363,366,369,372,375,378,381,384,387,390,393,396,399],{"id":81,"html":82,"type":83},"b1","\u003Cp>You build a login endpoint. It checks the password, and now you need to remember that this particular visitor is logged in the next time their browser shows up — which, on a server, is a completely new, unrelated request. Reach for \u003Ccode>useState\u003C\u002Fcode> and it resets every time, because it only lives for one server render. Reach for a module-scope variable and you&#39;ve just reintroduced the cross-request leak this series already covered. So you either stand up a database table for sessions, or reach for a third-party module to seal a cookie for you.\u003C\u002Fp>","paragraph",{"id":85,"html":86,"type":83},"b2","\u003Cp>As of Nuxt 4.6, released October 5, 2026, you don&#39;t need either. Nuxt core now ships \u003Ccode>useSession\u003C\u002Fcode>, a server session that lives entirely inside a tamper-proof cookie — no session store, no database row, no Redis.\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 why \u003Ccode>useState\u003C\u002Fcode> and a module-scope variable both fail to &quot;remember&quot; a visitor across real, separate requests","Set \u003Ccode>runtimeConfig.appSecret\u003C\u002Fcode> and understand what it&#39;s actually for","Use \u003Ccode>useSession\u003C\u002Fcode> to read, update, and clear session data from a server route","Explain \u003Cem>why\u003C\u002Fem> a sealed cookie is safe to trust — what happens when someone edits it","Avoid the one import mistake (\u003Ccode>NUXT_E8012\u003C\u002Fcode>) that trips people up migrating to \u003Ccode>nuxt\u002Fserver\u003C\u002Fcode>",{"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 read or can follow \u003Ca href=\"https:\u002F\u002Fdev.to\u002Fparsajiravand\u002Fnuxt-usestate-vs-ref-why-server-state-leaks-across-users-47n1\">the \u003Ccode>useState\u003C\u002Fcode> vs \u003Ccode>ref\u003C\u002Fcode> episode\u003C\u002Fa> — or at least you accept its conclusion: Nuxt&#39;s server has no memory of its own between requests unless you give it one on purpose. This article is about the tool Nuxt 4.6 gives you for that: a cookie-backed session. No prior auth-library experience needed.\u003C\u002Fp>",{"id":111,"html":112,"type":83},"b8","\u003Cp>This article is written against \u003Cstrong>Nuxt 4.6\u003C\u002Fstrong> (verified against the \u003Ccode>nuxt\u003C\u002Fcode> package&#39;s GitHub releases and npm dist-tags on October 17, 2026 — \u003Ccode>4.6.0\u003C\u002Fcode> shipped October 5, with patch releases \u003Ccode>4.6.1\u003C\u002Fcode> and \u003Ccode>4.6.2\u003C\u002Fcode> following; neither patch touches session handling, so everything below applies unchanged across all three). Code uses the Nuxt 4 \u003Ccode>app\u002F\u003C\u002Fcode> directory convention; \u003Ccode>server\u002F\u003C\u002Fcode> stays at the project root either way, same as every other episode in this series.\u003C\u002Fp>",{"id":114,"html":115,"text":115,"type":91,"level":31},"b9","Table of contents",{"id":117,"type":97,"items":118,"ordered":18},"b10",[119,120,121,122,123,124,125],"\u003Ca href=\"#the-problem-nothing-the-server-does-survives-the-request\">The problem: nothing the server does survives the request\u003C\u002Fa>","\u003Ca href=\"#the-mental-model-stateless-server-stateful-cookie\">The mental model: stateless server, stateful cookie\u003C\u002Fa>","\u003Ca href=\"#building-it-stage-by-stage\">Building it, stage by stage\u003C\u002Fa>","\u003Ca href=\"#edge-cases-and-gotchas\">Edge cases and gotchas\u003C\u002Fa>","\u003Ca href=\"#best-practices-usesession-vs-usestate-vs-a-database\">Best practices: useSession vs useState vs a database\u003C\u002Fa>","\u003Ca href=\"#faq\">FAQ\u003C\u002Fa>","\u003Ca href=\"#cheat-sheet\">Cheat sheet\u003C\u002Fa>",{"id":127,"html":128,"text":128,"type":91,"level":31},"b11","The problem: nothing the server does survives the request",{"id":130,"html":131,"type":83},"b12","\u003Cp>Here&#39;s a login handler that looks reasonable:\u003C\u002Fp>",{"id":133,"code":134,"type":135,"language":136,"highlight":137},"b13","\u002F\u002F server\u002Fapi\u002Flogin.post.ts\nimport { defineEventHandler, readBody } from 'nuxt\u002Fserver'\n\nlet currentUser: { id: number; name: string } | null = null \u002F\u002F wrong — see below\n\nexport default defineEventHandler(async (event) => {\n  const { username, password } = await readBody(event)\n  const user = await verifyCredentials(username, password)\n  currentUser = user \u002F\u002F \"remember\" them... where, exactly?\n  return { ok: true }\n})","code","ts",[],{"id":139,"code":140,"type":135,"language":136,"highlight":141},"b14","\u002F\u002F server\u002Fapi\u002Fme.get.ts\nimport { defineEventHandler } from 'nuxt\u002Fserver'\n\nexport default defineEventHandler(() => {\n  return { user: currentUser } \u002F\u002F undefined for the next visitor, or worse\n})",[],{"id":143,"html":144,"type":83},"b15","\u003Cp>This is the same bug the \u003Ccode>useState\u003C\u002Fcode> episode walked through: \u003Ccode>currentUser\u003C\u002Fcode> is a module-scope variable, created once when the server process starts, shared by every request that process ever handles. In development, with one tab open, it looks like it works — you log in, you call \u003Ccode>\u002Fapi\u002Fme\u003C\u002Fcode>, you get your user back. In production, with concurrent traffic, a second visitor&#39;s request can run between the two and overwrite \u003Ccode>currentUser\u003C\u002Fcode> before you read it. You&#39;d see someone else&#39;s session, or a login that silently &quot;loses&quot; itself under load.\u003C\u002Fp>",{"id":146,"html":147,"type":83},"b16","\u003Cp>The instinct to fix this with \u003Ccode>useState\u003C\u002Fcode> doesn&#39;t work either, for a different reason: \u003Ccode>useState\u003C\u002Fcode> isolates data \u003Cem>within a single server-side render\u003C\u002Fem> — it exists to get data safely into the SSR payload for one request&#39;s Vue app instance, and that instance is thrown away the moment the response is sent. It was never meant to persist between two separate HTTP requests made minutes apart; it isn&#39;t that kind of tool, and calling it from \u003Ccode>server\u002F\u003C\u002Fcode> doesn&#39;t even work (there&#39;s no Vue app instance there — that&#39;s the \u003Ca href=\"https:\u002F\u002Fdev.to\u002Fparsajiravand\u002Fnuxt-server-routes-explained-how-nitro-builds-your-api-9m2\">Nitro server routes episode\u003C\u002Fa>&#39;s subject).\u003C\u002Fp>",{"id":149,"html":150,"type":83},"b17","\u003Cp>What you actually need is something that survives between requests \u003Cem>without\u003C\u002Fem> living in server memory at all — because the server might restart, and in production it&#39;s rarely a single process.\u003C\u002Fp>",{"id":152,"html":153,"text":153,"type":91,"level":31},"b18","The mental model: stateless server, stateful cookie",{"id":155,"html":156,"type":83},"b19","\u003Cp>\u003Cstrong>The mental model:\u003C\u002Fstrong> a Nitro request handler, by itself, is stateless — it has no memory of any earlier request, by design. The only way information crosses from one request to the next is if the \u003Cem>client\u003C\u002Fem> carries it back: either a reference (a session ID the server looks up in a database) or the data itself, sealed so the client can carry it without being able to read or forge it.\u003C\u002Fp>",{"id":158,"html":159,"type":83},"b20","\u003Cp>\u003Ccode>useSession\u003C\u002Fcode>, new in Nuxt 4.6 via the \u003Ccode>nuxt\u002Fserver\u003C\u002Fcode> import source, is the second approach. When you call it, Nuxt:\u003C\u002Fp>",{"id":161,"type":97,"items":162,"ordered":17},"b21",[163,164,165,166],"reads the session cookie on the incoming request, if one exists;","\u003Cstrong>unseals\u003C\u002Fstrong> it — decrypts it and verifies it hasn&#39;t been tampered with, using a key derived from your app&#39;s secret;","hands you a \u003Ccode>session.data\u003C\u002Fcode> object to read and a \u003Ccode>session.update()\u003C\u002Fcode> to change it;","on \u003Ccode>update()\u003C\u002Fcode>, \u003Cstrong>reseals\u003C\u002Fstrong> the (possibly new) data into a fresh cookie and attaches it to the response.",{"id":168,"html":169,"type":83},"b22","\u003Cp>Nothing is stored on the server. The cookie \u003Cem>is\u003C\u002Fem> the session. That&#39;s also why it needs a secret: without one, anyone could write their own cookie and hand your server whatever session data they wanted.\u003C\u002Fp>",{"id":171,"html":172,"type":173},"b23","\u003Cp>\u003Cstrong>A term of art:\u003C\u002Fstrong> &quot;sealing&quot; here means authenticated encryption — the data is both encrypted (unreadable without the key) and signed (any edit invalidates it). Nuxt&#39;s session helpers use the \u003Ca href=\"https:\u002F\u002Fgithub.com\u002Funjs\u002Fh3\">\u003Ccode>iron\u003C\u002Fcode>\u003C\u002Fa> sealing format via \u003Ccode>h3\u003C\u002Fcode>, the same primitive underneath, so a one-character edit to the cookie doesn&#39;t produce \u003Cem>different\u003C\u002Fem> session data — it just fails to unseal at all, and the request is treated as having no session.\u003C\u002Fp>\n","quote",{"id":175,"html":176,"text":176,"type":91,"level":31},"b24","Building it, stage by stage",{"id":178,"html":179,"text":179,"type":91,"level":43},"b25","Stage 1 — the one secret your whole app needs",{"id":181,"html":182,"type":83},"b26","\u003Cp>Nuxt 4.6 introduces a single root secret: \u003Ccode>runtimeConfig.appSecret\u003C\u002Fcode>, set via the \u003Ccode>NUXT_APP_SECRET\u003C\u002Fcode> environment variable. Session sealing, and any future module that needs a secret, derives its own purpose-specific key from it internally — you configure one value, not a different secret per feature.\u003C\u002Fp>",{"id":184,"code":185,"type":135,"language":186,"highlight":187},"b27","# generate one, once, per environment\nopenssl rand -base64 32","bash",[],{"id":189,"code":190,"type":135,"language":186,"highlight":191},"b28","# .env (never commit this)\nNUXT_APP_SECRET=the-output-of-the-command-above",[],{"id":193,"html":194,"type":83},"b29","\u003Cp>\u003Cstrong>Key concept:\u003C\u002Fstrong> in local development, if you haven&#39;t set one, Nuxt generates and saves a secret for you automatically and warns the first time something derives a key from it. That convenience is dev-only — \u003Cstrong>a production build never auto-generates a secret.\u003C\u002Fstrong> If you deploy without setting \u003Ccode>NUXT_APP_SECRET\u003C\u002Fcode>, you&#39;re deploying without one, not getting a free one; treat it the same as any other missing required credential, and set it before your first production deploy, not after something breaks.\u003C\u002Fp>",{"id":196,"html":197,"text":197,"type":91,"level":43},"b30","Stage 2 — reading and writing session data",{"id":199,"code":200,"type":135,"language":136,"highlight":201},"b31","\u002F\u002F server\u002Fapi\u002Fvisit.get.ts\nimport { defineEventHandler, useSession } from 'nuxt\u002Fserver'\n\nexport default defineEventHandler(async (event) => {\n  const session = await useSession\u003C{ visits: number }>(event)\n\n  await session.update({ visits: (session.data.visits ?? 0) + 1 })\n\n  return { visits: session.data.visits }\n})",[],{"id":203,"html":204,"type":83},"b32","\u003Cp>Call \u003Ccode>\u002Fapi\u002Fvisit\u003C\u002Fcode> once and you get \u003Ccode>{ &quot;visits&quot;: 1 }\u003C\u002Fcode>. Refresh and it climbs: \u003Ccode>2\u003C\u002Fcode>, \u003Ccode>3\u003C\u002Fcode>, \u003Ccode>4\u003C\u002Fcode> — across completely separate requests, with no database anywhere in this code. Open the same route in a different browser (no shared cookie) and it starts at \u003Ccode>1\u003C\u002Fcode> again, independently. That&#39;s the whole point: the state travels with the \u003Cem>visitor\u003C\u002Fem>, sealed into their own cookie, not with the server process.\u003C\u002Fp>",{"id":206,"html":207,"type":83},"b33","\u003Cp>\u003Cstrong>Key concept:\u003C\u002Fstrong> unlike using \u003Ccode>h3\u003C\u002Fcode>&#39;s session utility directly — where you must pass a \u003Ccode>password\u003C\u002Fcode> of at least 32 characters yourself — Nuxt&#39;s wrapper derives that password from \u003Ccode>runtimeConfig.appSecret\u003C\u002Fcode> for you. \u003Ccode>appSecret\u003C\u002Fcode> really is the only secret you manage.\u003C\u002Fp>",{"id":209,"html":210,"text":210,"type":91,"level":43},"b34","Stage 3 — logging someone in, and out",{"id":212,"code":213,"type":135,"language":136,"highlight":214},"b35","\u002F\u002F server\u002Fapi\u002Flogin.post.ts\nimport { defineEventHandler, readBody, useSession } from 'nuxt\u002Fserver'\n\nexport default defineEventHandler(async (event) => {\n  const { username, password } = await readBody\u003C{ username: string; password: string }>(event)\n  const user = await verifyCredentials(username, password) \u002F\u002F your own check\n\n  const session = await useSession\u003C{ userId: number }>(event)\n  await session.update({ userId: user.id })\n\n  return { ok: true }\n})",[],{"id":216,"code":217,"type":135,"language":136,"highlight":218},"b36","\u002F\u002F server\u002Fapi\u002Flogout.post.ts\nimport { defineEventHandler, useSession } from 'nuxt\u002Fserver'\n\nexport default defineEventHandler(async (event) => {\n  const session = await useSession(event)\n  await session.clear() \u002F\u002F removes the session data AND the cookie\n  return { ok: true }\n})",[],{"id":220,"html":221,"type":83},"b37","\u003Cp>Every later request that carries the resulting cookie can call \u003Ccode>useSession(event)\u003C\u002Fcode> again and read \u003Ccode>session.data.userId\u003C\u002Fcode> straight back — no lookup, no round trip to anywhere.\u003C\u002Fp>",{"id":223,"html":224,"text":225,"type":91,"level":43},"b38","Stage 4 — the migration trap: \u003Ccode>NUXT_E8012\u003C\u002Fcode>","Stage 4 — the migration trap: NUXTE8012",{"id":227,"html":228,"type":83},"b39","\u003Cp>\u003Ccode>nuxt\u002Fserver\u003C\u002Fcode> is a new, parallel import source alongside the classic auto-imported \u003Ccode>server\u002F\u003C\u002Fcode> utilities (which come from \u003Ccode>h3\u003C\u002Fcode> under the hood either way). They are not meant to be mixed in the same handler:\u003C\u002Fp>",{"id":230,"code":231,"type":135,"language":136,"highlight":232},"b40","\u002F\u002F ❌ mixes an auto-imported helper with an explicit nuxt\u002Fserver import\nimport { useSession } from 'nuxt\u002Fserver'\n\nexport default defineEventHandler(async (event) => { \u002F\u002F defineEventHandler here is auto-imported, not explicit\n  const session = await useSession(event)\n  \u002F\u002F …\n})",[],{"id":234,"html":235,"type":83},"b41","\u003Cp>Doing this throws \u003Ccode>NUXT_E8012\u003C\u002Fcode> at build or runtime, depending on what&#39;s mixed. The fix is mechanical: once a handler touches \u003Ccode>nuxt\u002Fserver\u003C\u002Fcode>, import everything it uses — \u003Ccode>defineEventHandler\u003C\u002Fcode>, \u003Ccode>readBody\u003C\u002Fcode>, \u003Ccode>useSession\u003C\u002Fcode>, whatever else — from \u003Ccode>nuxt\u002Fserver\u003C\u002Fcode> explicitly, as every example above does. Nothing requires you to migrate existing handlers that only use the classic auto-imports; the rule only bites when you start mixing the two styles in the same file.\u003C\u002Fp>",{"id":237,"html":238,"text":238,"type":91,"level":31},"b42","Edge cases and gotchas",{"id":240,"type":97,"items":241,"ordered":18},"b43",[242,243,244,245,246],"\u003Cstrong>The cookie has a size limit, because it&#39;s a cookie.\u003C\u002Fstrong> A database-backed session can hold anything; a sealed-cookie session is bounded by how much a browser will send back in a header (a few KB, practically). Keep \u003Ccode>session.data\u003C\u002Fcode> small — an ID, a role, a couple of flags — not a shopping cart&#39;s full contents.","\u003Cstrong>It isn&#39;t reachable from a Vue component.\u003C\u002Fstrong> \u003Ccode>useSession\u003C\u002Fcode> lives in \u003Ccode>nuxt\u002Fserver\u003C\u002Fcode>, for server routes and middleware only. To get session-derived data into a page, expose it from an endpoint and read it with \u003Ccode>useFetch\u003C\u002Fcode>\u002F\u003Ccode>useAsyncData\u003C\u002Fcode> — the same request\u002Fdedupe rules from \u003Ca href=\"https:\u002F\u002Fdev.to\u002Fparsajiravand\u002Fuseasyncdata-keys-in-nuxt-caching-dedupe-the-sharing-bug-el1\">the \u003Ccode>useAsyncData\u003C\u002Fcode> episode\u003C\u002Fa> apply.","\u003Cstrong>Rotating \u003Ccode>appSecret\u003C\u002Fcode> logs everyone out at once.\u003C\u002Fstrong> Every existing cookie was sealed with keys derived from the old secret; once the secret changes, every one of them fails to unseal and is treated as &quot;no session.&quot; That&#39;s correct behavior, not a bug — but plan rotations knowing the blast radius.","\u003Cstrong>This scales horizontally for free.\u003C\u002Fstrong> Because there&#39;s no server-side store, any number of server instances can validate the same session as long as they share the same \u003Ccode>appSecret\u003C\u002Fcode> — no shared Redis, no sticky sessions, nothing to keep in sync.","\u003Cstrong>\u003Ccode>maxAge\u003C\u002Fcode> and \u003Ccode>idleTimeout\u003C\u002Fcode> are separate knobs.\u003C\u002Fstrong> \u003Ccode>maxAge\u003C\u002Fcode> is an absolute lifetime from creation; \u003Ccode>idleTimeout\u003C\u002Fcode> is a sliding window measured from the last request that touched the session. Set at least \u003Ccode>maxAge\u003C\u002Fcode> explicitly — don&#39;t rely on defaults you haven&#39;t checked for your use case.",{"id":248,"html":249,"text":249,"type":91,"level":31},"b44","Best practices: useSession vs useState vs a database",{"id":251,"head":252,"rows":255,"type":268},"b45",[253,254],"You need…","Reach for",[256,259,262,265],[257,258],"Data to reach the client within \u003Cem>this\u003C\u002Fem> render&#39;s payload, safely per-request","\u003Ccode>useState\u003C\u002Fcode>",[260,261],"A small, server-only fact that must survive \u003Cem>across separate requests\u003C\u002Fem> from the same visitor (login status, a role, a cart ID)","\u003Ccode>useSession\u003C\u002Fcode>",[263,264],"Data too large for a cookie, shared across devices, or queryable\u002Fauditable on its own","A real database, keyed by a session ID",[266,267],"A value every request needs and that never changes per-visitor","\u003Ccode>runtimeConfig\u003C\u002Fcode>, not a session at all","table",{"id":270,"html":271,"type":83},"b46","\u003Cp>Never put secrets \u003Cem>inside\u003C\u002Fem> session data expecting them to stay hidden from the visitor who owns that session — sealing hides the data from everyone else, not from the person whose cookie it is. And never reach for a module-scope variable to &quot;remember&quot; anything about a specific visitor; that bug is exactly what this series&#39; \u003Ca href=\"https:\u002F\u002Fdev.to\u002Fparsajiravand\u002Fnuxt-usestate-vs-ref-why-server-state-leaks-across-users-47n1\">\u003Ccode>useState\u003C\u002Fcode> vs \u003Ccode>ref\u003C\u002Fcode>\u003C\u002Fa> episode walked through.\u003C\u002Fp>",{"id":273,"html":274,"text":274,"type":91,"level":31},"b47","FAQ",{"id":276,"html":277,"text":278,"type":91,"level":43},"b48","Is Nuxt&#39;s \u003Ccode>useSession\u003C\u002Fcode> the same as h3&#39;s \u003Ccode>useSession\u003C\u002Fcode>?","Is Nuxt's useSession the same as h3's useSession?",{"id":280,"html":281,"type":83},"b49","\u003Cp>It&#39;s built on the same underlying session and sealing mechanism from \u003Ccode>h3\u003C\u002Fcode>, exposed through the new \u003Ccode>nuxt\u002Fserver\u003C\u002Fcode> import source so it&#39;s portable across Nitro v2, Nitro v3, and the experimental Vite-based server Nuxt is also building. The difference that matters day to day is that Nuxt fills in the password for you from \u003Ccode>runtimeConfig.appSecret\u003C\u002Fcode>.\u003C\u002Fp>",{"id":283,"html":284,"text":285,"type":91,"level":43},"b50","Do I still need a module like \u003Ccode>nuxt-auth-utils\u003C\u002Fcode>?","Do I still need a module like nuxt-auth-utils?",{"id":287,"html":288,"type":83},"b51","\u003Cp>For the cookie-sealing mechanism itself, no — that&#39;s now in Nuxt core. A full auth module still earns its keep for things core doesn&#39;t do: OAuth provider flows, refresh-token rotation, pre-built login UI. Read what a module adds on top before assuming you still need it for sessions alone.\u003C\u002Fp>",{"id":290,"html":291,"text":292,"type":91,"level":43},"b52","What happens if I forget to set \u003Ccode>NUXT_APP_SECRET\u003C\u002Fcode> in production?","What happens if I forget to set NUXTAPPSECRET in production?",{"id":294,"html":295,"type":83},"b53","\u003Cp>Nothing generates one for you — that only happens in local development. Treat a missing \u003Ccode>appSecret\u003C\u002Fcode> the same as any other missing required environment variable: set it before your first deploy, store it the way you store any other secret, and never commit it.\u003C\u002Fp>",{"id":297,"html":298,"text":298,"type":91,"level":43},"b54","Can a visitor edit their own session cookie to grant themselves more access?",{"id":300,"html":301,"type":83},"b55","\u003Cp>No, as long as sealing is doing its job: editing even one character of a sealed cookie breaks the seal, and \u003Ccode>useSession\u003C\u002Fcode> treats an unsealable cookie as no session at all — not as the edited data. They can delete their session or get logged out; they can&#39;t forge a different one without your \u003Ccode>appSecret\u003C\u002Fcode>.\u003C\u002Fp>",{"id":303,"html":304,"text":304,"type":91,"level":43},"b56","Does this replace a database for user accounts?",{"id":306,"html":307,"type":83},"b57","\u003Cp>No. \u003Ccode>useSession\u003C\u002Fcode> holds the small, ephemeral fact that a particular request belongs to a particular already-authenticated visitor. The accounts themselves — usernames, hashed passwords, profile data — still belong in a real datastore; the session just carries a reference to one, such as a user ID.\u003C\u002Fp>",{"id":309,"html":310,"text":310,"type":91,"level":31},"b58","Cheat sheet",{"id":312,"code":313,"type":135,"language":136,"highlight":314},"b59","\u002F\u002F 1. One secret, once per environment\n\u002F\u002F .env → NUXT_APP_SECRET=\u003Copenssl rand -base64 32>\n\u002F\u002F (dev: auto-generated if missing. prod builds: never auto-generated — you must set it.)\n\n\u002F\u002F 2. Always import everything from nuxt\u002Fserver once you touch it (avoids NUXT_E8012)\nimport { defineEventHandler, readBody, useSession } from 'nuxt\u002Fserver'\n\nexport default defineEventHandler(async (event) => {\n  \u002F\u002F 3. Read + create\n  const session = await useSession\u003C{ userId?: number; visits?: number }>(event)\n\n  \u002F\u002F 4. Update (reseals the cookie automatically)\n  await session.update({ visits: (session.data.visits ?? 0) + 1 })\n\n  \u002F\u002F 5. Read back\n  const current = session.data\n\n  \u002F\u002F 6. Log out \u002F destroy\n  \u002F\u002F await session.clear()\n\n  return current\n})",[],{"id":316,"head":317,"rows":321,"type":268},"b60",[318,319,320],"Task","Do this","Notes",[322,326,330,334,338,342],[323,324,325],"Set the one required secret","\u003Ccode>NUXT_APP_SECRET\u003C\u002Fcode> env var","Dev auto-generates one; prod never does",[327,328,329],"Read session data","\u003Ccode>const s = await useSession(event)\u003C\u002Fcode> → \u003Ccode>s.data\u003C\u002Fcode>","Returns \u003Ccode>{}\u003C\u002Fcode> if none exists yet",[331,332,333],"Change session data","\u003Ccode>await s.update({ key: value })\u003C\u002Fcode>","Merges and reseals the cookie",[335,336,337],"End the session","\u003Ccode>await s.clear()\u003C\u002Fcode>","Removes data and the cookie",[339,340,341],"Reach it from a Vue component","Expose it from an endpoint, call with \u003Ccode>useFetch\u003C\u002Fcode>","\u003Ccode>useSession\u003C\u002Fcode> is server-only",[343,344,345],"Avoid \u003Ccode>NUXT_E8012\u003C\u002Fcode>","Import \u003Cem>everything\u003C\u002Fem> in that handler from \u003Ccode>nuxt\u002Fserver\u003C\u002Fcode>","Don&#39;t mix with classic auto-imports",{"id":347,"html":348,"text":348,"type":91,"level":31},"b61","Key takeaways",{"id":350,"type":97,"items":351,"ordered":18},"b62",[352,353,354,355,356],"A Nitro request handler has no memory between requests on its own — that&#39;s the same lesson as the cross-request state leak, applied constructively this time.","Nuxt 4.6&#39;s \u003Ccode>useSession\u003C\u002Fcode>, from \u003Ccode>nuxt\u002Fserver\u003C\u002Fcode>, seals session data into the cookie itself: no database, no Redis, and it scales across instances for free.","\u003Ccode>runtimeConfig.appSecret\u003C\u002Fcode> (\u003Ccode>NUXT_APP_SECRET\u003C\u002Fcode>) is the one secret everything derives from — set it yourself before deploying; Nuxt only auto-generates one in development.","Sealing means tampering breaks the cookie instead of forging new data — that&#39;s \u003Cem>why\u003C\u002Fem> it&#39;s safe to trust.","\u003Ccode>useSession\u003C\u002Fcode> and \u003Ccode>useState\u003C\u002Fcode> solve different problems: one survives across real requests, the other gets data into a single render&#39;s payload. Mixing them up is the bug.",{"id":358,"html":359,"type":83},"b63","\u003C!-- playground:start -->",{"id":361,"html":362,"text":362,"type":91,"level":31},"b64","🎮 Try it yourself",{"id":364,"html":365,"type":83},"b65","\u003Cp>\u003Cstrong>\u003Ca href=\"https:\u002F\u002Fbestpractic.org\u002Fblog\u002Fnuxt-weekly-usesession-sealed-sessions\u002Fplayground\">▶️ Open the interactive playground →\u003C\u002Fa>\u003C\u002Fstrong>\u003C\u002Fp>",{"id":367,"html":368,"type":83},"b66","\u003Cp>\u003Cem>Runs right in your browser — poke at it and watch the concept react live.\u003C\u002Fem>\u003C\u002Fp>",{"id":370,"html":371,"type":83},"b67","\u003C!-- playground:end -->",{"id":373,"html":374,"type":83},"b68","\u003C!-- quiz:start -->",{"id":376,"html":377,"text":377,"type":91,"level":31},"b69","🧠 Test yourself",{"id":379,"html":380,"type":83},"b70","\u003Cp>Think it clicked? \u003Cstrong>\u003Ca href=\"https:\u002F\u002Fbestpractic.org\u002Fblog\u002Fnuxt-weekly-usesession-sealed-sessions\u002Fquiz\">Take the 9-question quiz →\u003C\u002Fa>\u003C\u002Fstrong>\u003C\u002Fp>",{"id":382,"html":383,"type":83},"b71","\u003Cp>\u003Cem>Instant feedback, a hint on every question, and an explanation for each answer — right or wrong.\u003C\u002Fem>\u003C\u002Fp>",{"id":385,"html":386,"type":83},"b72","\u003C!-- quiz:end -->",{"id":388,"html":389,"type":83},"b73","\u003Cp>That cart-losing, login-dropping bug from the top of this article doesn&#39;t need a database migration or a Redis instance to fix anymore — \u003Ccode>runtimeConfig.appSecret\u003C\u002Fcode> and \u003Ccode>useSession\u003C\u002Fcode> are already sitting in Nuxt 4.6. What&#39;s the smallest piece of per-visitor state your own app is currently faking with something that doesn&#39;t actually survive a request? Drop it in the comments — there&#39;s a good chance \u003Ccode>useSession\u003C\u002Fcode> is the fix.\u003C\u002Fp>",{"id":391,"type":392},"b74","divider",{"id":394,"html":395,"type":83},"b75","\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":397,"html":398,"type":83},"b76","\u003Cp>\u003Cem>Thanks for reading! Let&#39;s stay connected:\u003C\u002Fem>\u003C\u002Fp>",{"id":400,"type":97,"items":401,"ordered":18},"b77",[402,403,404],"⭐ \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>","You build a login endpoint. It checks the password, and now you need to remember that this particular visitor is logged in the next time their browser shows up — which, on a server, is a completely new, unrelated request. Reach for `useState` and it resets every time, because it only lives for one server render. Reach for a module-scope variable and you've just reintroduced the cross-request leak this series already covered. So you either stand up a database table for sessions, or reach for a third-party module to seal a cookie for you.\n\nAs of Nuxt 4.6, released October 5, 2026, you don't need either. Nuxt core now ships `useSession`, a server session that lives entirely inside a tamper-proof cookie — no session store, no database row, no Redis.\n\n## What you'll learn\n\nBy the end of this article you'll be able to:\n\n- Explain why `useState` and a module-scope variable both fail to \"remember\" a visitor across real, separate requests\n- Set `runtimeConfig.appSecret` and understand what it's actually for\n- Use `useSession` to read, update, and clear session data from a server route\n- Explain *why* a sealed cookie is safe to trust — what happens when someone edits it\n- Avoid the one import mistake (`NUXT_E8012`) that trips people up migrating to `nuxt\u002Fserver`\n\n## Who this is for\n\nYou've read or can follow [the `useState` vs `ref` episode](https:\u002F\u002Fdev.to\u002Fparsajiravand\u002Fnuxt-usestate-vs-ref-why-server-state-leaks-across-users-47n1) — or at least you accept its conclusion: Nuxt's server has no memory of its own between requests unless you give it one on purpose. This article is about the tool Nuxt 4.6 gives you for that: a cookie-backed session. No prior auth-library experience needed.\n\nThis article is written against **Nuxt 4.6** (verified against the `nuxt` package's GitHub releases and npm dist-tags on October 17, 2026 — `4.6.0` shipped October 5, with patch releases `4.6.1` and `4.6.2` following; neither patch touches session handling, so everything below applies unchanged across all three). Code uses the Nuxt 4 `app\u002F` directory convention; `server\u002F` stays at the project root either way, same as every other episode in this series.\n\n## Table of contents\n\n- [The problem: nothing the server does survives the request](#the-problem-nothing-the-server-does-survives-the-request)\n- [The mental model: stateless server, stateful cookie](#the-mental-model-stateless-server-stateful-cookie)\n- [Building it, stage by stage](#building-it-stage-by-stage)\n- [Edge cases and gotchas](#edge-cases-and-gotchas)\n- [Best practices: useSession vs useState vs a database](#best-practices-usesession-vs-usestate-vs-a-database)\n- [FAQ](#faq)\n- [Cheat sheet](#cheat-sheet)\n\n## The problem: nothing the server does survives the request\n\nHere's a login handler that looks reasonable:\n\n```ts\n\u002F\u002F server\u002Fapi\u002Flogin.post.ts\nimport { defineEventHandler, readBody } from 'nuxt\u002Fserver'\n\nlet currentUser: { id: number; name: string } | null = null \u002F\u002F wrong — see below\n\nexport default defineEventHandler(async (event) => {\n  const { username, password } = await readBody(event)\n  const user = await verifyCredentials(username, password)\n  currentUser = user \u002F\u002F \"remember\" them... where, exactly?\n  return { ok: true }\n})\n```\n\n```ts\n\u002F\u002F server\u002Fapi\u002Fme.get.ts\nimport { defineEventHandler } from 'nuxt\u002Fserver'\n\nexport default defineEventHandler(() => {\n  return { user: currentUser } \u002F\u002F undefined for the next visitor, or worse\n})\n```\n\nThis is the same bug the `useState` episode walked through: `currentUser` is a module-scope variable, created once when the server process starts, shared by every request that process ever handles. In development, with one tab open, it looks like it works — you log in, you call `\u002Fapi\u002Fme`, you get your user back. In production, with concurrent traffic, a second visitor's request can run between the two and overwrite `currentUser` before you read it. You'd see someone else's session, or a login that silently \"loses\" itself under load.\n\nThe instinct to fix this with `useState` doesn't work either, for a different reason: `useState` isolates data *within a single server-side render* — it exists to get data safely into the SSR payload for one request's Vue app instance, and that instance is thrown away the moment the response is sent. It was never meant to persist between two separate HTTP requests made minutes apart; it isn't that kind of tool, and calling it from `server\u002F` doesn't even work (there's no Vue app instance there — that's the [Nitro server routes episode](https:\u002F\u002Fdev.to\u002Fparsajiravand\u002Fnuxt-server-routes-explained-how-nitro-builds-your-api-9m2)'s subject).\n\nWhat you actually need is something that survives between requests *without* living in server memory at all — because the server might restart, and in production it's rarely a single process.\n\n## The mental model: stateless server, stateful cookie\n\n**The mental model:** a Nitro request handler, by itself, is stateless — it has no memory of any earlier request, by design. The only way information crosses from one request to the next is if the *client* carries it back: either a reference (a session ID the server looks up in a database) or the data itself, sealed so the client can carry it without being able to read or forge it.\n\n`useSession`, new in Nuxt 4.6 via the `nuxt\u002Fserver` import source, is the second approach. When you call it, Nuxt:\n\n1. reads the session cookie on the incoming request, if one exists;\n2. **unseals** it — decrypts it and verifies it hasn't been tampered with, using a key derived from your app's secret;\n3. hands you a `session.data` object to read and a `session.update()` to change it;\n4. on `update()`, **reseals** the (possibly new) data into a fresh cookie and attaches it to the response.\n\nNothing is stored on the server. The cookie *is* the session. That's also why it needs a secret: without one, anyone could write their own cookie and hand your server whatever session data they wanted.\n\n> **A term of art:** \"sealing\" here means authenticated encryption — the data is both encrypted (unreadable without the key) and signed (any edit invalidates it). Nuxt's session helpers use the [`iron`](https:\u002F\u002Fgithub.com\u002Funjs\u002Fh3) sealing format via `h3`, the same primitive underneath, so a one-character edit to the cookie doesn't produce *different* session data — it just fails to unseal at all, and the request is treated as having no session.\n\n## Building it, stage by stage\n\n### Stage 1 — the one secret your whole app needs\n\nNuxt 4.6 introduces a single root secret: `runtimeConfig.appSecret`, set via the `NUXT_APP_SECRET` environment variable. Session sealing, and any future module that needs a secret, derives its own purpose-specific key from it internally — you configure one value, not a different secret per feature.\n\n```bash\n# generate one, once, per environment\nopenssl rand -base64 32\n```\n\n```bash\n# .env (never commit this)\nNUXT_APP_SECRET=the-output-of-the-command-above\n```\n\n**Key concept:** in local development, if you haven't set one, Nuxt generates and saves a secret for you automatically and warns the first time something derives a key from it. That convenience is dev-only — **a production build never auto-generates a secret.** If you deploy without setting `NUXT_APP_SECRET`, you're deploying without one, not getting a free one; treat it the same as any other missing required credential, and set it before your first production deploy, not after something breaks.\n\n### Stage 2 — reading and writing session data\n\n```ts\n\u002F\u002F server\u002Fapi\u002Fvisit.get.ts\nimport { defineEventHandler, useSession } from 'nuxt\u002Fserver'\n\nexport default defineEventHandler(async (event) => {\n  const session = await useSession\u003C{ visits: number }>(event)\n\n  await session.update({ visits: (session.data.visits ?? 0) + 1 })\n\n  return { visits: session.data.visits }\n})\n```\n\nCall `\u002Fapi\u002Fvisit` once and you get `{ \"visits\": 1 }`. Refresh and it climbs: `2`, `3`, `4` — across completely separate requests, with no database anywhere in this code. Open the same route in a different browser (no shared cookie) and it starts at `1` again, independently. That's the whole point: the state travels with the *visitor*, sealed into their own cookie, not with the server process.\n\n**Key concept:** unlike using `h3`'s session utility directly — where you must pass a `password` of at least 32 characters yourself — Nuxt's wrapper derives that password from `runtimeConfig.appSecret` for you. `appSecret` really is the only secret you manage.\n\n### Stage 3 — logging someone in, and out\n\n```ts\n\u002F\u002F server\u002Fapi\u002Flogin.post.ts\nimport { defineEventHandler, readBody, useSession } from 'nuxt\u002Fserver'\n\nexport default defineEventHandler(async (event) => {\n  const { username, password } = await readBody\u003C{ username: string; password: string }>(event)\n  const user = await verifyCredentials(username, password) \u002F\u002F your own check\n\n  const session = await useSession\u003C{ userId: number }>(event)\n  await session.update({ userId: user.id })\n\n  return { ok: true }\n})\n```\n\n```ts\n\u002F\u002F server\u002Fapi\u002Flogout.post.ts\nimport { defineEventHandler, useSession } from 'nuxt\u002Fserver'\n\nexport default defineEventHandler(async (event) => {\n  const session = await useSession(event)\n  await session.clear() \u002F\u002F removes the session data AND the cookie\n  return { ok: true }\n})\n```\n\nEvery later request that carries the resulting cookie can call `useSession(event)` again and read `session.data.userId` straight back — no lookup, no round trip to anywhere.\n\n### Stage 4 — the migration trap: `NUXT_E8012`\n\n`nuxt\u002Fserver` is a new, parallel import source alongside the classic auto-imported `server\u002F` utilities (which come from `h3` under the hood either way). They are not meant to be mixed in the same handler:\n\n```ts\n\u002F\u002F ❌ mixes an auto-imported helper with an explicit nuxt\u002Fserver import\nimport { useSession } from 'nuxt\u002Fserver'\n\nexport default defineEventHandler(async (event) => { \u002F\u002F defineEventHandler here is auto-imported, not explicit\n  const session = await useSession(event)\n  \u002F\u002F …\n})\n```\n\nDoing this throws `NUXT_E8012` at build or runtime, depending on what's mixed. The fix is mechanical: once a handler touches `nuxt\u002Fserver`, import everything it uses — `defineEventHandler`, `readBody`, `useSession`, whatever else — from `nuxt\u002Fserver` explicitly, as every example above does. Nothing requires you to migrate existing handlers that only use the classic auto-imports; the rule only bites when you start mixing the two styles in the same file.\n\n## Edge cases and gotchas\n\n- **The cookie has a size limit, because it's a cookie.** A database-backed session can hold anything; a sealed-cookie session is bounded by how much a browser will send back in a header (a few KB, practically). Keep `session.data` small — an ID, a role, a couple of flags — not a shopping cart's full contents.\n- **It isn't reachable from a Vue component.** `useSession` lives in `nuxt\u002Fserver`, for server routes and middleware only. To get session-derived data into a page, expose it from an endpoint and read it with `useFetch`\u002F`useAsyncData` — the same request\u002Fdedupe rules from [the `useAsyncData` episode](https:\u002F\u002Fdev.to\u002Fparsajiravand\u002Fuseasyncdata-keys-in-nuxt-caching-dedupe-the-sharing-bug-el1) apply.\n- **Rotating `appSecret` logs everyone out at once.** Every existing cookie was sealed with keys derived from the old secret; once the secret changes, every one of them fails to unseal and is treated as \"no session.\" That's correct behavior, not a bug — but plan rotations knowing the blast radius.\n- **This scales horizontally for free.** Because there's no server-side store, any number of server instances can validate the same session as long as they share the same `appSecret` — no shared Redis, no sticky sessions, nothing to keep in sync.\n- **`maxAge` and `idleTimeout` are separate knobs.** `maxAge` is an absolute lifetime from creation; `idleTimeout` is a sliding window measured from the last request that touched the session. Set at least `maxAge` explicitly — don't rely on defaults you haven't checked for your use case.\n\n## Best practices: useSession vs useState vs a database\n\n| You need… | Reach for |\n| --- | --- |\n| Data to reach the client within *this* render's payload, safely per-request | `useState` |\n| A small, server-only fact that must survive *across separate requests* from the same visitor (login status, a role, a cart ID) | `useSession` |\n| Data too large for a cookie, shared across devices, or queryable\u002Fauditable on its own | A real database, keyed by a session ID |\n| A value every request needs and that never changes per-visitor | `runtimeConfig`, not a session at all |\n\nNever put secrets *inside* session data expecting them to stay hidden from the visitor who owns that session — sealing hides the data from everyone else, not from the person whose cookie it is. And never reach for a module-scope variable to \"remember\" anything about a specific visitor; that bug is exactly what this series' [`useState` vs `ref`](https:\u002F\u002Fdev.to\u002Fparsajiravand\u002Fnuxt-usestate-vs-ref-why-server-state-leaks-across-users-47n1) episode walked through.\n\n## FAQ\n\n### Is Nuxt's `useSession` the same as h3's `useSession`?\n\nIt's built on the same underlying session and sealing mechanism from `h3`, exposed through the new `nuxt\u002Fserver` import source so it's portable across Nitro v2, Nitro v3, and the experimental Vite-based server Nuxt is also building. The difference that matters day to day is that Nuxt fills in the password for you from `runtimeConfig.appSecret`.\n\n### Do I still need a module like `nuxt-auth-utils`?\n\nFor the cookie-sealing mechanism itself, no — that's now in Nuxt core. A full auth module still earns its keep for things core doesn't do: OAuth provider flows, refresh-token rotation, pre-built login UI. Read what a module adds on top before assuming you still need it for sessions alone.\n\n### What happens if I forget to set `NUXT_APP_SECRET` in production?\n\nNothing generates one for you — that only happens in local development. Treat a missing `appSecret` the same as any other missing required environment variable: set it before your first deploy, store it the way you store any other secret, and never commit it.\n\n### Can a visitor edit their own session cookie to grant themselves more access?\n\nNo, as long as sealing is doing its job: editing even one character of a sealed cookie breaks the seal, and `useSession` treats an unsealable cookie as no session at all — not as the edited data. They can delete their session or get logged out; they can't forge a different one without your `appSecret`.\n\n### Does this replace a database for user accounts?\n\nNo. `useSession` holds the small, ephemeral fact that a particular request belongs to a particular already-authenticated visitor. The accounts themselves — usernames, hashed passwords, profile data — still belong in a real datastore; the session just carries a reference to one, such as a user ID.\n\n## Cheat sheet\n\n```ts\n\u002F\u002F 1. One secret, once per environment\n\u002F\u002F .env → NUXT_APP_SECRET=\u003Copenssl rand -base64 32>\n\u002F\u002F (dev: auto-generated if missing. prod builds: never auto-generated — you must set it.)\n\n\u002F\u002F 2. Always import everything from nuxt\u002Fserver once you touch it (avoids NUXT_E8012)\nimport { defineEventHandler, readBody, useSession } from 'nuxt\u002Fserver'\n\nexport default defineEventHandler(async (event) => {\n  \u002F\u002F 3. Read + create\n  const session = await useSession\u003C{ userId?: number; visits?: number }>(event)\n\n  \u002F\u002F 4. Update (reseals the cookie automatically)\n  await session.update({ visits: (session.data.visits ?? 0) + 1 })\n\n  \u002F\u002F 5. Read back\n  const current = session.data\n\n  \u002F\u002F 6. Log out \u002F destroy\n  \u002F\u002F await session.clear()\n\n  return current\n})\n```\n\n| Task | Do this | Notes |\n| --- | --- | --- |\n| Set the one required secret | `NUXT_APP_SECRET` env var | Dev auto-generates one; prod never does |\n| Read session data | `const s = await useSession(event)` → `s.data` | Returns `{}` if none exists yet |\n| Change session data | `await s.update({ key: value })` | Merges and reseals the cookie |\n| End the session | `await s.clear()` | Removes data and the cookie |\n| Reach it from a Vue component | Expose it from an endpoint, call with `useFetch` | `useSession` is server-only |\n| Avoid `NUXT_E8012` | Import *everything* in that handler from `nuxt\u002Fserver` | Don't mix with classic auto-imports |\n\n## Key takeaways\n\n- A Nitro request handler has no memory between requests on its own — that's the same lesson as the cross-request state leak, applied constructively this time.\n- Nuxt 4.6's `useSession`, from `nuxt\u002Fserver`, seals session data into the cookie itself: no database, no Redis, and it scales across instances for free.\n- `runtimeConfig.appSecret` (`NUXT_APP_SECRET`) is the one secret everything derives from — set it yourself before deploying; Nuxt only auto-generates one in development.\n- Sealing means tampering breaks the cookie instead of forging new data — that's *why* it's safe to trust.\n- `useSession` and `useState` solve different problems: one survives across real requests, the other gets data into a single render's payload. Mixing them up is the bug.\n\n\u003C!-- playground:start -->\n\n## 🎮 Try it yourself\n\n**[▶️ Open the interactive playground →](https:\u002F\u002Fbestpractic.org\u002Fblog\u002Fnuxt-weekly-usesession-sealed-sessions\u002Fplayground)**\n\n_Runs right in your browser — poke at it and watch the concept react live._\n\n\u003C!-- playground:end -->\n\n\u003C!-- quiz:start -->\n\n## 🧠 Test yourself\n\nThink it clicked? **[Take the 9-question quiz →](https:\u002F\u002Fbestpractic.org\u002Fblog\u002Fnuxt-weekly-usesession-sealed-sessions\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\nThat cart-losing, login-dropping bug from the top of this article doesn't need a database migration or a Redis instance to fix anymore — `runtimeConfig.appSecret` and `useSession` are already sitting in Nuxt 4.6. What's the smallest piece of per-visitor state your own app is currently faking with something that doesn't actually survive a request? Drop it in the comments — there's a good chance `useSession` is the fix.\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":407,"description":49},"https:\u002F\u002Fbestpractic.org\u002Fblog\u002Fnuxt-weekly-usesession-sealed-sessions","01a143f6-4be7-7006-8c06-ede958f95167",{"name":410,"part":411,"total":411,"items":412},"Nuxt Deep Dive",7,[413,418,423,427,431,435,439],{"slug":414,"title":415,"publishedAt":416,"readingMinutes":417},"nuxt-weekly-cross-request-state-leak","Nuxt useState vs ref(): Why Server State Leaks Across Users","2026-08-30T11:37:11.335Z",14,{"slug":419,"title":420,"publishedAt":421,"readingMinutes":422},"nuxt-weekly-useasyncdata-keys-dedupe","useAsyncData Keys in Nuxt: Caching, Dedupe & the Sharing Bug","2026-09-13T11:26:28.014Z",15,{"slug":424,"title":425,"publishedAt":426,"readingMinutes":417},"nuxt-weekly-hydration-mismatch","Nuxt Hydration Mismatch: Why It Happens and How to Fix It","2026-09-20T16:26:05.191Z",{"slug":428,"title":429,"publishedAt":430,"readingMinutes":51},"nuxt-weekly-nitro-server-routes","Nuxt Server Routes Explained: How Nitro Builds Your API","2026-09-29T12:10:53.560Z",{"slug":432,"title":433,"publishedAt":434,"readingMinutes":417},"nuxt-weekly-ssr-streaming-route-rules","Nuxt 4.5 SSR Streaming: The Route Rules That Disable It","2026-10-04T12:03:48.007Z",{"slug":436,"title":437,"publishedAt":438,"readingMinutes":417},"nuxt-weekly-4-migration-breaking-changes","Nuxt 4 Migration: The Breaking Changes That Actually Bite","2026-10-11T12:26:26.492Z",{"slug":46,"title":47,"publishedAt":52,"readingMinutes":51},{"id":441,"locked":18},"01a143f6-4c3b-707a-b235-2bf113b0bc35",[443],{"id":444,"slug":46,"title":445,"_count":446},"01a143f6-4c6e-745b-ae07-2ee46b3ee4b1","Nuxt useSession and sealed sessions",{"questions":447},9,[449],{"locale":13,"slug":46},{"id":444,"slug":46,"title":445,"_count":451,"questionCount":447},{"questions":447},[453,457,459,463,465,469,473,477,481,484,488,492],{"slug":454,"name":455,"articles":456},"webdev","Webdev",144,{"slug":69,"name":70,"articles":458},117,{"slug":460,"name":461,"articles":462},"frontend","Frontend",87,{"slug":72,"name":73,"articles":464},55,{"slug":466,"name":467,"articles":468},"css","Css",44,{"slug":470,"name":471,"articles":472},"performance","Performance",23,{"slug":474,"name":475,"articles":476},"typescript","Typescript",19,{"slug":478,"name":479,"articles":480},"react","React",18,{"slug":482,"name":483,"articles":51},"browser","Browser",{"slug":485,"name":486,"articles":487},"node","Node",12,{"slug":489,"name":490,"articles":491},"programming","Programming",11,{"slug":493,"name":494,"articles":495},"accessibility","Accessibility",10,[],{"items":498,"meta":605},[499,511,526,543,560,575,590],{"id":45,"slug":46,"title":47,"subtitle":48,"excerpt":49,"coverUrl":50,"locale":13,"readingMinutes":51,"publishedAt":52,"viewCount":500,"likeCount":19,"commentCount":19,"author":501,"vertical":502,"topic":503,"tags":504,"_count":509,"playground":510,"hasQuiz":17,"hasPlayground":17},36,{"id":55,"name":56,"username":57,"avatarUrl":48,"headline":58},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":61,"name":62},[505,506,507,508],{"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":512,"slug":436,"title":437,"subtitle":48,"excerpt":513,"coverUrl":514,"locale":13,"readingMinutes":417,"publishedAt":438,"viewCount":515,"likeCount":19,"commentCount":19,"author":516,"vertical":517,"topic":518,"tags":519,"_count":524,"playground":525,"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",366,{"id":55,"name":56,"username":57,"avatarUrl":48,"headline":58},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":61,"name":62},[520,521,522,523],{"slug":61,"name":62,"color":48},{"slug":69,"name":70,"color":48},{"slug":72,"name":73,"color":48},{"slug":454,"name":455,"color":48},{"assessments":75},{"slug":436},{"id":527,"slug":432,"title":433,"subtitle":48,"excerpt":528,"coverUrl":529,"locale":13,"readingMinutes":417,"publishedAt":434,"viewCount":530,"likeCount":19,"commentCount":19,"author":531,"vertical":532,"topic":533,"tags":534,"_count":541,"playground":542,"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",370,{"id":55,"name":56,"username":57,"avatarUrl":48,"headline":58},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":61,"name":62},[535,536,539,540],{"slug":61,"name":62,"color":48},{"slug":537,"name":538,"color":48},"ssr","SSR",{"slug":69,"name":70,"color":48},{"slug":72,"name":73,"color":48},{"assessments":75},{"slug":432},{"id":544,"slug":428,"title":429,"subtitle":48,"excerpt":545,"coverUrl":546,"locale":13,"readingMinutes":51,"publishedAt":430,"viewCount":547,"likeCount":19,"commentCount":19,"author":548,"vertical":549,"topic":550,"tags":551,"_count":558,"playground":559,"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",491,{"id":55,"name":56,"username":57,"avatarUrl":48,"headline":58},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":61,"name":62},[552,553,556,557],{"slug":61,"name":62,"color":48},{"slug":554,"name":555,"color":48},"backend","Backend",{"slug":69,"name":70,"color":48},{"slug":72,"name":73,"color":48},{"assessments":75},{"slug":428},{"id":561,"slug":424,"title":425,"subtitle":48,"excerpt":562,"coverUrl":563,"locale":13,"readingMinutes":417,"publishedAt":426,"viewCount":564,"likeCount":19,"commentCount":19,"author":565,"vertical":566,"topic":567,"tags":568,"_count":573,"playground":574,"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",478,{"id":55,"name":56,"username":57,"avatarUrl":48,"headline":58},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":61,"name":62},[569,570,571,572],{"slug":61,"name":62,"color":48},{"slug":537,"name":538,"color":48},{"slug":69,"name":70,"color":48},{"slug":72,"name":73,"color":48},{"assessments":75},{"slug":424},{"id":576,"slug":419,"title":420,"subtitle":48,"excerpt":577,"coverUrl":578,"locale":13,"readingMinutes":422,"publishedAt":421,"viewCount":579,"likeCount":19,"commentCount":19,"author":580,"vertical":581,"topic":582,"tags":583,"_count":588,"playground":589,"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",472,{"id":55,"name":56,"username":57,"avatarUrl":48,"headline":58},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":61,"name":62},[584,585,586,587],{"slug":61,"name":62,"color":48},{"slug":537,"name":538,"color":48},{"slug":69,"name":70,"color":48},{"slug":72,"name":73,"color":48},{"assessments":75},{"slug":419},{"id":591,"slug":414,"title":415,"subtitle":48,"excerpt":592,"coverUrl":593,"locale":13,"readingMinutes":417,"publishedAt":416,"viewCount":594,"likeCount":19,"commentCount":19,"author":595,"vertical":596,"topic":597,"tags":598,"_count":603,"playground":604,"hasQuiz":17,"hasPlayground":17},"01a04446-3f08-701c-8cdc-e5ab5e87086e","A module-scope ref() in Nuxt is shared by every request that hits your server. Learn why useState isolates state per user, and how to fix the leak.","\u002Fmedia\u002Fcovers\u002Fnuxt-weekly-cross-request-state-leak.png",507,{"id":55,"name":56,"username":57,"avatarUrl":48,"headline":58},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":61,"name":62},[599,600,601,602],{"slug":61,"name":62,"color":48},{"slug":537,"name":538,"color":48},{"slug":69,"name":70,"color":48},{"slug":72,"name":73,"color":48},{"assessments":75},{"slug":414},{"page":75,"perPage":411,"total":411,"totalPages":75},"\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>\u003C\u002Fdiv>\u003Cdiv class=\"shj-code\">\u003Cspan class=\"shj-cmnt\">\u002F\u002F server\u002Fapi\u002Flogin.post.ts\n\u003C\u002Fspan>\u003Cspan class=\"shj-kwd\">import\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan> defineEventHandler\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> readBody \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\">let\u003C\u002Fspan> currentUser\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan> id\u003Cspan class=\"shj-type\">: number\u003C\u002Fspan>; name\u003Cspan class=\"shj-type\">: string\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan> \u003Cspan class=\"shj-oper\">|\u003C\u002Fspan> \u003Cspan class=\"shj-num\">null\u003C\u002Fspan> \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> \u003Cspan class=\"shj-num\">null\u003C\u002Fspan> \u003Cspan class=\"shj-cmnt\">\u002F\u002F wrong — see below\n\u003C\u002Fspan>\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> username\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> password \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan> \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">await\u003C\u002Fspan> \u003Cspan class=\"shj-func\">readBody\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>event\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\n  \u003Cspan class=\"shj-kwd\">const\u003C\u002Fspan> user \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">await\u003C\u002Fspan> \u003Cspan class=\"shj-func\">verifyCredentials\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>username\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> password\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\n  currentUser \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> user \u003Cspan class=\"shj-cmnt\">\u002F\u002F \"remember\" them... where, exactly?\n\u003C\u002Fspan>  \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-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>\u003C\u002Fdiv>\u003Cdiv class=\"shj-code\">\u003Cspan class=\"shj-cmnt\">\u002F\u002F server\u002Fapi\u002Fme.get.ts\n\u003C\u002Fspan>\u003Cspan class=\"shj-kwd\">import\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan> defineEventHandler \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>\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-bracket\">{\u003C\u002Fspan> user\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> currentUser \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan> \u003Cspan class=\"shj-cmnt\">\u002F\u002F undefined for the next visitor, or worse\n\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\u003C\u002Fdiv>\u003C\u002Fdiv>\u003C\u002Fdiv>","\u003Cdiv class=\"shj shj-lang-bash shj-multiline\" data-lang=\"bash\">\u003Cdiv class=\"shj-scroll\">\u003Cdiv class=\"shj-numbers\">\u003Cdiv>1\u003C\u002Fdiv>\u003Cdiv>2\u003C\u002Fdiv>\u003C\u002Fdiv>\u003Cdiv class=\"shj-code\">\u003Cspan class=\"shj-cmnt\"># generate one, once, per environment\u003C\u002Fspan>\n\u003Cspan class=\"shj-func\">openssl\u003C\u002Fspan> rand\u003Cspan class=\"shj-kwd\"> -base\u003C\u002Fspan>64 \u003Cspan class=\"shj-num\">32\u003C\u002Fspan>\u003C\u002Fdiv>\u003C\u002Fdiv>\u003C\u002Fdiv>","\u003Cdiv class=\"shj shj-lang-bash shj-multiline\" data-lang=\"bash\">\u003Cdiv class=\"shj-scroll\">\u003Cdiv class=\"shj-numbers\">\u003Cdiv>1\u003C\u002Fdiv>\u003Cdiv>2\u003C\u002Fdiv>\u003C\u002Fdiv>\u003Cdiv class=\"shj-code\">\u003Cspan class=\"shj-cmnt\"># .env (never commit this)\u003C\u002Fspan>\n\u003Cspan class=\"shj-var\">NUXT_APP_SECRET\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">=\u003C\u002Fspan>the-output-of-the-command-above\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>\u003C\u002Fdiv>\u003Cdiv class=\"shj-code\">\u003Cspan class=\"shj-cmnt\">\u002F\u002F server\u002Fapi\u002Fvisit.get.ts\n\u003C\u002Fspan>\u003Cspan class=\"shj-kwd\">import\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan> defineEventHandler\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> session \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">await\u003C\u002Fspan> useSession\u003Cspan class=\"shj-oper\">&lt;\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan> visits\u003Cspan class=\"shj-type\">: number\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>event\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\n\n  \u003Cspan class=\"shj-kwd\">await\u003C\u002Fspan> session\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">update\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan> visits\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>session\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>data\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>visits \u003Cspan class=\"shj-oper\">??\u003C\u002Fspan> \u003Cspan class=\"shj-num\">0\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-oper\">+\u003C\u002Fspan> \u003Cspan class=\"shj-num\">1\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\n\n  \u003Cspan class=\"shj-kwd\">return\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan> visits\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> session\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>data\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>visits \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>\u003C\u002Fdiv>\u003Cdiv class=\"shj-code\">\u003Cspan class=\"shj-cmnt\">\u002F\u002F server\u002Fapi\u002Flogin.post.ts\n\u003C\u002Fspan>\u003Cspan class=\"shj-kwd\">import\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan> defineEventHandler\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> readBody\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> username\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> password \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan> \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">await\u003C\u002Fspan> readBody\u003Cspan class=\"shj-oper\">&lt;\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan> username\u003Cspan class=\"shj-type\">: string\u003C\u002Fspan>; password\u003Cspan class=\"shj-type\">: string\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>event\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\n  \u003Cspan class=\"shj-kwd\">const\u003C\u002Fspan> user \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">await\u003C\u002Fspan> \u003Cspan class=\"shj-func\">verifyCredentials\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>username\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> password\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-cmnt\">\u002F\u002F your own check\n\u003C\u002Fspan>\n  \u003Cspan class=\"shj-kwd\">const\u003C\u002Fspan> session \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">await\u003C\u002Fspan> useSession\u003Cspan class=\"shj-oper\">&lt;\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan> userId\u003Cspan class=\"shj-type\">: number\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>event\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\n  \u003Cspan class=\"shj-kwd\">await\u003C\u002Fspan> session\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">update\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan> userId\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> user\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>id \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\n\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-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 server\u002Fapi\u002Flogout.post.ts\n\u003C\u002Fspan>\u003Cspan class=\"shj-kwd\">import\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan> defineEventHandler\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> 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-bracket\">)\u003C\u002Fspan>\n  \u003Cspan class=\"shj-kwd\">await\u003C\u002Fspan> session\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">clear\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-cmnt\">\u002F\u002F removes the session data AND the cookie\n\u003C\u002Fspan>  \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-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>\u003C\u002Fdiv>\u003Cdiv class=\"shj-code\">\u003Cspan class=\"shj-cmnt\">\u002F\u002F ❌ mixes an auto-imported helper with an explicit nuxt\u002Fserver import\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-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> \u003Cspan class=\"shj-cmnt\">\u002F\u002F defineEventHandler here is auto-imported, not explicit\n\u003C\u002Fspan>  \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-bracket\">)\u003C\u002Fspan>\n  \u003Cspan class=\"shj-cmnt\">\u002F\u002F …\n\u003C\u002Fspan>\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>\u003Cdiv>14\u003C\u002Fdiv>\u003Cdiv>15\u003C\u002Fdiv>\u003Cdiv>16\u003C\u002Fdiv>\u003Cdiv>17\u003C\u002Fdiv>\u003Cdiv>18\u003C\u002Fdiv>\u003Cdiv>19\u003C\u002Fdiv>\u003Cdiv>20\u003C\u002Fdiv>\u003Cdiv>21\u003C\u002Fdiv>\u003Cdiv>22\u003C\u002Fdiv>\u003C\u002Fdiv>\u003Cdiv class=\"shj-code\">\u003Cspan class=\"shj-cmnt\">\u002F\u002F 1. One secret, once per environment\n\u003C\u002Fspan>\u003Cspan class=\"shj-cmnt\">\u002F\u002F .env → NUXT_APP_SECRET=&lt;openssl rand -base64 32&gt;\n\u003C\u002Fspan>\u003Cspan class=\"shj-cmnt\">\u002F\u002F (dev: auto-generated if missing. prod builds: never auto-generated — you must set it.)\n\u003C\u002Fspan>\n\u003Cspan class=\"shj-cmnt\">\u002F\u002F 2. Always import everything from nuxt\u002Fserver once you touch it (avoids NUXT_E8012)\n\u003C\u002Fspan>\u003Cspan class=\"shj-kwd\">import\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan> defineEventHandler\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> readBody\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-cmnt\">\u002F\u002F 3. Read + create\n\u003C\u002Fspan>  \u003Cspan class=\"shj-kwd\">const\u003C\u002Fspan> session \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">await\u003C\u002Fspan> useSession\u003Cspan class=\"shj-oper\">&lt;\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan> userId\u003Cspan class=\"shj-oper\">?:\u003C\u002Fspan> number; visits\u003Cspan class=\"shj-oper\">?:\u003C\u002Fspan> number \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>event\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\n\n  \u003Cspan class=\"shj-cmnt\">\u002F\u002F 4. Update (reseals the cookie automatically)\n\u003C\u002Fspan>  \u003Cspan class=\"shj-kwd\">await\u003C\u002Fspan> session\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">update\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan> visits\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>session\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>data\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>visits \u003Cspan class=\"shj-oper\">??\u003C\u002Fspan> \u003Cspan class=\"shj-num\">0\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-oper\">+\u003C\u002Fspan> \u003Cspan class=\"shj-num\">1\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\n\n  \u003Cspan class=\"shj-cmnt\">\u002F\u002F 5. Read back\n\u003C\u002Fspan>  \u003Cspan class=\"shj-kwd\">const\u003C\u002Fspan> current \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> session\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>data\n\n  \u003Cspan class=\"shj-cmnt\">\u002F\u002F 6. Log out \u002F destroy\n\u003C\u002Fspan>  \u003Cspan class=\"shj-cmnt\">\u002F\u002F await session.clear()\n\u003C\u002Fspan>\n  \u003Cspan class=\"shj-kwd\">return\u003C\u002Fspan> current\n\u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\u003C\u002Fdiv>\u003C\u002Fdiv>\u003C\u002Fdiv>",{"locked":18,"total":19,"comments":616},[]]