[{"data":1,"prerenderedAt":1432},["ShallowReactive",2],{"navigation:en":3,"\u002Fdocs\u002Fsaas-starter-kit\u002Ffeatures\u002Fdatabase:en":311},[4],{"title":5,"path":6,"stem":7,"children":8,"icon":10},"Nuxfire Docs","\u002Fdocs","docs\u002F0.index",[9,11,152],{"title":5,"path":6,"stem":7,"icon":10},"i-lucide-layout-grid",{"title":12,"path":13,"stem":14,"children":15,"icon":151},"SaaS Starter Kit","\u002Fdocs\u002Fsaas-starter-kit","docs\u002F1.saas-starter-kit\u002F0.index",[16,19,63,129],{"title":17,"path":13,"stem":14,"icon":18},"What is the SaaS Starter Kit?","i-lucide-info",{"title":20,"icon":21,"hide":22,"path":23,"stem":24,"children":25,"page":62},"Getting Started","i-lucide-play",true,"\u002Fdocs\u002Fsaas-starter-kit\u002Fgetting-started","docs\u002F1.saas-starter-kit\u002F1.getting-started",[26,30,34,38,42,46,50,54,58],{"title":27,"path":28,"stem":29},"Quick Start","\u002Fdocs\u002Fsaas-starter-kit\u002Fgetting-started\u002Fquick-start","docs\u002F1.saas-starter-kit\u002F1.getting-started\u002F0.quick-start",{"title":31,"path":32,"stem":33},"Installation","\u002Fdocs\u002Fsaas-starter-kit\u002Fgetting-started\u002Finstallation","docs\u002F1.saas-starter-kit\u002F1.getting-started\u002F1.installation",{"title":35,"path":36,"stem":37},"Cloudflare Access","\u002Fdocs\u002Fsaas-starter-kit\u002Fgetting-started\u002Fcloudflare-access","docs\u002F1.saas-starter-kit\u002F1.getting-started\u002F2.cloudflare-access",{"title":39,"path":40,"stem":41},"Configuration","\u002Fdocs\u002Fsaas-starter-kit\u002Fgetting-started\u002Fconfiguration","docs\u002F1.saas-starter-kit\u002F1.getting-started\u002F3.configuration",{"title":43,"path":44,"stem":45},"Secrets","\u002Fdocs\u002Fsaas-starter-kit\u002Fgetting-started\u002Fsecrets","docs\u002F1.saas-starter-kit\u002F1.getting-started\u002F4.secrets",{"title":47,"path":48,"stem":49},"Environment Variables","\u002Fdocs\u002Fsaas-starter-kit\u002Fgetting-started\u002Fenvironment-variables","docs\u002F1.saas-starter-kit\u002F1.getting-started\u002F5.environment-variables",{"title":51,"path":52,"stem":53},"Deploy","\u002Fdocs\u002Fsaas-starter-kit\u002Fgetting-started\u002Fdeploy","docs\u002F1.saas-starter-kit\u002F1.getting-started\u002F6.deploy",{"title":55,"path":56,"stem":57},"Local Development","\u002Fdocs\u002Fsaas-starter-kit\u002Fgetting-started\u002Flocal-development","docs\u002F1.saas-starter-kit\u002F1.getting-started\u002F7.local-development",{"title":59,"path":60,"stem":61},"Project Structure","\u002Fdocs\u002Fsaas-starter-kit\u002Fgetting-started\u002Fproject-structure","docs\u002F1.saas-starter-kit\u002F1.getting-started\u002F8.project-structure",false,{"title":64,"icon":65,"hide":22,"path":66,"stem":67,"children":68,"page":62},"Features","i-lucide-box","\u002Fdocs\u002Fsaas-starter-kit\u002Ffeatures","docs\u002F1.saas-starter-kit\u002F2.features",[69,73,77,81,85,89,93,97,101,105,109,113,117,121,125],{"title":70,"path":71,"stem":72},"Authentication","\u002Fdocs\u002Fsaas-starter-kit\u002Ffeatures\u002Fauth","docs\u002F1.saas-starter-kit\u002F2.features\u002F1.auth",{"title":74,"path":75,"stem":76},"Rate Limiting","\u002Fdocs\u002Fsaas-starter-kit\u002Ffeatures\u002Frate-limiting","docs\u002F1.saas-starter-kit\u002F2.features\u002F10.rate-limiting",{"title":78,"path":79,"stem":80},"WebSockets","\u002Fdocs\u002Fsaas-starter-kit\u002Ffeatures\u002Fwebsockets","docs\u002F1.saas-starter-kit\u002F2.features\u002F11.websockets",{"title":82,"path":83,"stem":84},"Blog","\u002Fdocs\u002Fsaas-starter-kit\u002Ffeatures\u002Fblog","docs\u002F1.saas-starter-kit\u002F2.features\u002F14.blog",{"title":86,"path":87,"stem":88},"Documentation","\u002Fdocs\u002Fsaas-starter-kit\u002Ffeatures\u002Fdocumentation","docs\u002F1.saas-starter-kit\u002F2.features\u002F15.documentation",{"title":90,"path":91,"stem":92},"Analytics","\u002Fdocs\u002Fsaas-starter-kit\u002Ffeatures\u002Fanalytics","docs\u002F1.saas-starter-kit\u002F2.features\u002F16.analytics",{"title":94,"path":95,"stem":96},"Platform Admin Console","\u002Fdocs\u002Fsaas-starter-kit\u002Ffeatures\u002Fplatform-admin","docs\u002F1.saas-starter-kit\u002F2.features\u002F17.platform-admin",{"title":98,"path":99,"stem":100},"Database","\u002Fdocs\u002Fsaas-starter-kit\u002Ffeatures\u002Fdatabase","docs\u002F1.saas-starter-kit\u002F2.features\u002F2.database",{"title":102,"path":103,"stem":104},"Payments & Billing","\u002Fdocs\u002Fsaas-starter-kit\u002Ffeatures\u002Fpayments","docs\u002F1.saas-starter-kit\u002F2.features\u002F3.payments",{"title":106,"path":107,"stem":108},"Emails","\u002Fdocs\u002Fsaas-starter-kit\u002Ffeatures\u002Femails","docs\u002F1.saas-starter-kit\u002F2.features\u002F4.emails",{"title":110,"path":111,"stem":112},"Notifications","\u002Fdocs\u002Fsaas-starter-kit\u002Ffeatures\u002Fnotifications","docs\u002F1.saas-starter-kit\u002F2.features\u002F5.notifications",{"title":114,"path":115,"stem":116},"Role-Based Access Control","\u002Fdocs\u002Fsaas-starter-kit\u002Ffeatures\u002Frbac","docs\u002F1.saas-starter-kit\u002F2.features\u002F6.rbac",{"title":118,"path":119,"stem":120},"Async Jobs","\u002Fdocs\u002Fsaas-starter-kit\u002Ffeatures\u002Fasync-jobs","docs\u002F1.saas-starter-kit\u002F2.features\u002F7.async-jobs",{"title":122,"path":123,"stem":124},"Storage","\u002Fdocs\u002Fsaas-starter-kit\u002Ffeatures\u002Fstorage","docs\u002F1.saas-starter-kit\u002F2.features\u002F8.storage",{"title":126,"path":127,"stem":128},"Internationalization","\u002Fdocs\u002Fsaas-starter-kit\u002Ffeatures\u002Finternationalization","docs\u002F1.saas-starter-kit\u002F2.features\u002F9.internationalization",{"title":130,"icon":131,"path":132,"stem":133,"children":134,"page":62},"Concepts","i-lucide-graduation-cap","\u002Fdocs\u002Fsaas-starter-kit\u002Fconcepts","docs\u002F1.saas-starter-kit\u002F3.concepts",[135,139,143,147],{"title":136,"path":137,"stem":138},"Technology Stack","\u002Fdocs\u002Fsaas-starter-kit\u002Fconcepts\u002Ftech-stack","docs\u002F1.saas-starter-kit\u002F3.concepts\u002F0.tech-stack",{"title":140,"path":141,"stem":142},"SST.dev","\u002Fdocs\u002Fsaas-starter-kit\u002Fconcepts\u002Fsst","docs\u002F1.saas-starter-kit\u002F3.concepts\u002F1.sst",{"title":144,"path":145,"stem":146},"NuxtHub","\u002Fdocs\u002Fsaas-starter-kit\u002Fconcepts\u002Fnuxt-hub","docs\u002F1.saas-starter-kit\u002F3.concepts\u002F2.nuxt-hub",{"title":148,"path":149,"stem":150},"Design System & Branding","\u002Fdocs\u002Fsaas-starter-kit\u002Fconcepts\u002Fdesign-system","docs\u002F1.saas-starter-kit\u002F3.concepts\u002F3.design-system","i-lucide-zap",{"title":153,"path":154,"stem":155,"children":156,"icon":159},"Fireskills","\u002Fdocs\u002Ffireskills","docs\u002F2.fireskills\u002F0.index",[157,160,219,249,275,293],{"title":158,"path":154,"stem":155,"icon":159},"What is Fireskills?","i-lucide-flame",{"title":20,"icon":21,"hide":22,"path":161,"stem":162,"children":163,"page":62},"\u002Fdocs\u002Ffireskills\u002Fgetting-started","docs\u002F2.fireskills\u002F1.getting-started",[164,167,171,175,179,183,187,191,195,199,203,207,211,215],{"title":27,"path":165,"stem":166},"\u002Fdocs\u002Ffireskills\u002Fgetting-started\u002Fquick-start","docs\u002F2.fireskills\u002F1.getting-started\u002F00.quick-start",{"title":168,"path":169,"stem":170},"Workflow & Governance","\u002Fdocs\u002Ffireskills\u002Fgetting-started\u002Fworkflow","docs\u002F2.fireskills\u002F1.getting-started\u002F01.workflow",{"title":172,"path":173,"stem":174},"Project Context","\u002Fdocs\u002Ffireskills\u002Fgetting-started\u002Fproject-context","docs\u002F2.fireskills\u002F1.getting-started\u002F02.project-context",{"title":176,"path":177,"stem":178},"Step 0: Setup","\u002Fdocs\u002Ffireskills\u002Fgetting-started\u002Fstep-setup","docs\u002F2.fireskills\u002F1.getting-started\u002F03.step-setup",{"title":180,"path":181,"stem":182},"Step 1: Inbox","\u002Fdocs\u002Ffireskills\u002Fgetting-started\u002Fstep-inbox","docs\u002F2.fireskills\u002F1.getting-started\u002F04.step-inbox",{"title":184,"path":185,"stem":186},"Step 2: Backlog","\u002Fdocs\u002Ffireskills\u002Fgetting-started\u002Fstep-backlog","docs\u002F2.fireskills\u002F1.getting-started\u002F05.step-backlog",{"title":188,"path":189,"stem":190},"Step 3: Specify","\u002Fdocs\u002Ffireskills\u002Fgetting-started\u002Fstep-specify","docs\u002F2.fireskills\u002F1.getting-started\u002F06.step-specify",{"title":192,"path":193,"stem":194},"Step 4: Validate","\u002Fdocs\u002Ffireskills\u002Fgetting-started\u002Fstep-validate","docs\u002F2.fireskills\u002F1.getting-started\u002F07.step-validate",{"title":196,"path":197,"stem":198},"Step 5: Tasks","\u002Fdocs\u002Ffireskills\u002Fgetting-started\u002Fstep-tasks","docs\u002F2.fireskills\u002F1.getting-started\u002F08.step-tasks",{"title":200,"path":201,"stem":202},"Step 6: TDD\u002FBDD","\u002Fdocs\u002Ffireskills\u002Fgetting-started\u002Fstep-tdd-bdd","docs\u002F2.fireskills\u002F1.getting-started\u002F09.step-tdd-bdd",{"title":204,"path":205,"stem":206},"Step 7: Implement","\u002Fdocs\u002Ffireskills\u002Fgetting-started\u002Fstep-implement","docs\u002F2.fireskills\u002F1.getting-started\u002F10.step-implement",{"title":208,"path":209,"stem":210},"Step 8: Documentator","\u002Fdocs\u002Ffireskills\u002Fgetting-started\u002Fstep-documentator","docs\u002F2.fireskills\u002F1.getting-started\u002F11.step-documentator",{"title":212,"path":213,"stem":214},"Late Changes","\u002Fdocs\u002Ffireskills\u002Fgetting-started\u002Flate-changes","docs\u002F2.fireskills\u002F1.getting-started\u002F12.late-changes",{"title":216,"path":217,"stem":218},"Other Entry Points","\u002Fdocs\u002Ffireskills\u002Fgetting-started\u002Fother-entry-points","docs\u002F2.fireskills\u002F1.getting-started\u002F13.other-entry-points",{"title":220,"icon":221,"hide":22,"path":222,"stem":223,"children":224,"page":62},"Methodology","i-lucide-git-branch","\u002Fdocs\u002Ffireskills\u002Fmethodology","docs\u002F2.fireskills\u002F2.methodology",[225,229,233,237,241,245],{"title":226,"path":227,"stem":228},"The Three Acts","\u002Fdocs\u002Ffireskills\u002Fmethodology\u002Fthree-acts","docs\u002F2.fireskills\u002F2.methodology\u002F0.three-acts",{"title":230,"path":231,"stem":232},"The Three Gates","\u002Fdocs\u002Ffireskills\u002Fmethodology\u002Fthree-gates","docs\u002F2.fireskills\u002F2.methodology\u002F1.three-gates",{"title":234,"path":235,"stem":236},"The Canonical Spec","\u002Fdocs\u002Ffireskills\u002Fmethodology\u002Fcanonical-spec","docs\u002F2.fireskills\u002F2.methodology\u002F2.canonical-spec",{"title":238,"path":239,"stem":240},"Lifecycle States","\u002Fdocs\u002Ffireskills\u002Fmethodology\u002Flifecycle-states","docs\u002F2.fireskills\u002F2.methodology\u002F3.lifecycle-states",{"title":242,"path":243,"stem":244},"Effort Scale","\u002Fdocs\u002Ffireskills\u002Fmethodology\u002Feffort-scale","docs\u002F2.fireskills\u002F2.methodology\u002F4.effort-scale",{"title":246,"path":247,"stem":248},"Milestones & Governance","\u002Fdocs\u002Ffireskills\u002Fmethodology\u002Fgovernance-and-milestones","docs\u002F2.fireskills\u002F2.methodology\u002F5.governance-and-milestones",{"title":250,"icon":251,"hide":22,"path":252,"stem":253,"children":254,"page":62},"Skills","i-lucide-layers","\u002Fdocs\u002Ffireskills\u002Fskills","docs\u002F2.fireskills\u002F3.skills",[255,259,263,267,271],{"title":256,"path":257,"stem":258},"Catalog","\u002Fdocs\u002Ffireskills\u002Fskills\u002Fcatalog","docs\u002F2.fireskills\u002F3.skills\u002F0.catalog",{"title":260,"path":261,"stem":262},"Act I: Define","\u002Fdocs\u002Ffireskills\u002Fskills\u002Fact-1-define","docs\u002F2.fireskills\u002F3.skills\u002F1.act-1-define",{"title":264,"path":265,"stem":266},"Act II: Design & Prove","\u002Fdocs\u002Ffireskills\u002Fskills\u002Fact-2-design-prove","docs\u002F2.fireskills\u002F3.skills\u002F2.act-2-design-prove",{"title":268,"path":269,"stem":270},"Act III: Deliver","\u002Fdocs\u002Ffireskills\u002Fskills\u002Fact-3-deliver","docs\u002F2.fireskills\u002F3.skills\u002F3.act-3-deliver",{"title":272,"path":273,"stem":274},"Governance & Decisions","\u002Fdocs\u002Ffireskills\u002Fskills\u002Fgovernance-and-decisions","docs\u002F2.fireskills\u002F3.skills\u002F4.governance-and-decisions",{"title":276,"icon":277,"hide":22,"path":278,"stem":279,"children":280,"page":62},"Specialists","i-lucide-sparkles","\u002Fdocs\u002Ffireskills\u002Fspecialists","docs\u002F2.fireskills\u002F4.specialists",[281,285,289],{"title":282,"path":283,"stem":284},"Overview","\u002Fdocs\u002Ffireskills\u002Fspecialists\u002Foverview","docs\u002F2.fireskills\u002F4.specialists\u002F0.overview",{"title":286,"path":287,"stem":288},"Nuxfire Stack","\u002Fdocs\u002Ffireskills\u002Fspecialists\u002Fnuxfire-stack","docs\u002F2.fireskills\u002F4.specialists\u002F1.nuxfire-stack",{"title":290,"path":291,"stem":292},"Engineering Specialists","\u002Fdocs\u002Ffireskills\u002Fspecialists\u002Fengineering-specialists","docs\u002F2.fireskills\u002F4.specialists\u002F2.engineering-specialists",{"title":294,"icon":295,"hide":22,"path":296,"stem":297,"children":298,"page":62},"CLI & Tooling","i-lucide-terminal","\u002Fdocs\u002Ffireskills\u002Fcli","docs\u002F2.fireskills\u002F5.cli",[299,303,307],{"title":300,"path":301,"stem":302},"CLI Overview","\u002Fdocs\u002Ffireskills\u002Fcli\u002Foverview","docs\u002F2.fireskills\u002F5.cli\u002F0.overview",{"title":304,"path":305,"stem":306},"Commands","\u002Fdocs\u002Ffireskills\u002Fcli\u002Fcommands","docs\u002F2.fireskills\u002F5.cli\u002F1.commands",{"title":308,"path":309,"stem":310},"Safety Scripts","\u002Fdocs\u002Ffireskills\u002Fcli\u002Fgovernance-scripts","docs\u002F2.fireskills\u002F5.cli\u002F2.governance-scripts",{"id":312,"title":98,"body":313,"description":1427,"extension":1428,"meta":1429,"navigation":22,"path":99,"seo":1430,"stem":100,"__hash__":1431},"docs\u002Fdocs\u002F1.saas-starter-kit\u002F2.features\u002F2.database.md",{"type":314,"value":315,"toc":1415},"minimark",[316,339,344,349,381,425,494,498,504,734,737,757,761,775,1001,1008,1012,1015,1241,1245,1268,1339,1346,1350,1376,1380,1387,1411],[317,318,319,320,324,325,332,333,338],"p",{},"Nuxfire uses ",[321,322,323],"strong",{},"Postgres"," as its primary database, accessed from Cloudflare Workers through ",[326,327,331],"a",{"href":328,"rel":329},"https:\u002F\u002Fdevelopers.cloudflare.com\u002Fhyperdrive\u002F",[330],"nofollow","Hyperdrive",", with ",[326,334,337],{"href":335,"rel":336},"https:\u002F\u002Form.drizzle.team\u002F",[330],"Drizzle ORM",".",[340,341,343],"h2",{"id":342},"database-architecture","Database Architecture",[345,346,348],"h3",{"id":347},"_1-schema-definition","1. Schema Definition",[317,350,351,352,356,357,360,361,364,365,368,369,372,373,376,377,380],{},"Table definitions are split across ",[353,354,355],"code",{},"apps\u002Ffunctions\u002Fsrc\u002Fdatabase\u002Fcore-schema.ts"," and each layer's own ",[353,358,359],{},"server\u002Fdatabase\u002Fschema.ts",", all using Drizzle's Postgres dialect (",[353,362,363],{},"pgTable",", ",[353,366,367],{},"pgEnum","). ",[353,370,371],{},"apps\u002Ffunctions\u002Fsrc\u002Fdatabase\u002Fschema.ts"," is not where tables are defined — it's a hand-maintained aggregator that re-exports all of them (",[353,374,375],{},"export * from \".\u002Fcore-schema\""," plus one line per layer) behind the single import path ",[353,378,379],{},"@nuxfire\u002Ffunctions\u002Fschema",":",[382,383,384,393,401,409,417],"ul",{},[385,386,387,392],"li",{},[321,388,389],{},[353,390,391],{},"core-schema.ts"," — Users, Jobs, Logs, Dead Letter Records, Waitlist Emails.",[385,394,395,400],{},[321,396,397],{},[353,398,399],{},"layers\u002Fteams\u002Fserver\u002Fdatabase\u002Fschema.ts"," — Teams, Team Memberships, Team Invites, Roles, Permissions.",[385,402,403,408],{},[321,404,405],{},[353,406,407],{},"layers\u002Fbilling\u002Fserver\u002Fdatabase\u002Fschema.ts"," — Plans, Benefits, BenefitsOnPlan, Subscriptions.",[385,410,411,416],{},[321,412,413],{},[353,414,415],{},"layers\u002Fnotifications\u002Fserver\u002Fdatabase\u002Fschema.ts"," — Notifications.",[385,418,419,424],{},[321,420,421],{},[353,422,423],{},"layers\u002Fadmin\u002Fserver\u002Fdatabase\u002Fschema.ts"," — Platform Admins, Audit Logs.",[317,426,427,428,431,432,435,436,439,440,364,443,364,446,364,449,452,453,455,456,458,459,462,463,466,467,455,469,472,473,476,477,479,480,483,484,472,487,472,490,493],{},"Turning a feature layer off (",[353,429,430],{},"config.ts","'s ",[353,433,434],{},"layers.features",") removes its pages\u002Fcomponents from the Nuxt bundle, never its tables — ",[353,437,438],{},"schema.ts"," includes every layer's schema unconditionally. The four core layers (",[353,441,442],{},"teams",[353,444,445],{},"billing",[353,447,448],{},"notifications",[353,450,451],{},"admin",") aren't toggleable at all. Several of these files import back and forth across the aggregator\u002Flayer boundary (e.g. ",[353,454,391],{}," imports ",[353,457,442],{}," from ",[353,460,461],{},"layers\u002Fteams"," for ",[353,464,465],{},"Job","'s foreign key, while ",[353,468,461],{},[353,470,471],{},"users","\u002F",[353,474,475],{},"jobs"," back from ",[353,478,391],{},") — always through a package specifier (",[353,481,482],{},"@nuxfire\u002Flayer-teams\u002F...","), never a relative path, since SST's own Worker bundler (used to deploy ",[353,485,486],{},"cron.ts",[353,488,489],{},"jobs-consumer.ts",[353,491,492],{},"dlq-consumer.ts",") fails to resolve a relative path reaching outside its package.",[345,495,497],{"id":496},"_2-infrastructure-setup","2. Infrastructure Setup",[317,499,500,501,380],{},"Workers can't hold a direct TCP connection to Postgres, so the database is reached through Hyperdrive, configured in ",[353,502,503],{},"infra\u002Fdatabase.ts",[505,506,511],"pre",{"className":507,"code":508,"filename":503,"language":509,"meta":510,"style":510},"language-typescript shiki shiki-themes material-theme-lighter github-light github-dark monokai","export const database = new sst.cloudflare.Hyperdrive(\"AppDB\", {\n  origin: {\n    scheme: \"postgres\",\n    host: secret.DatabaseHost.value,\n    port: secret.DatabasePort.value.apply(Number),\n    database: secret.DatabaseName.value,\n    user: secret.DatabaseUser.value,\n    password: secret.DatabasePassword.value,\n  },\n});\n","typescript","",[353,512,513,571,582,601,624,653,674,695,716,722],{"__ignoreMap":510},[514,515,518,522,526,530,534,537,541,544,547,549,552,555,559,563,565,568],"span",{"class":516,"line":517},"line",1,[514,519,521],{"class":520},"sRxSC","export",[514,523,525],{"class":524},"srJo8"," const",[514,527,529],{"class":528},"s91G_"," database",[514,531,533],{"class":532},"sGXK2"," =",[514,535,536],{"class":532}," new",[514,538,540],{"class":539},"ss--_"," sst",[514,542,338],{"class":543},"swvn1",[514,545,546],{"class":539},"cloudflare",[514,548,338],{"class":543},[514,550,331],{"class":551},"sD0ED",[514,553,554],{"class":539},"(",[514,556,558],{"class":557},"siCPE","\"",[514,560,562],{"class":561},"sLACW","AppDB",[514,564,558],{"class":557},[514,566,567],{"class":543},",",[514,569,570],{"class":543}," {\n",[514,572,574,578,580],{"class":516,"line":573},2,[514,575,577],{"class":576},"squCx","  origin",[514,579,380],{"class":543},[514,581,570],{"class":543},[514,583,585,588,590,593,596,598],{"class":516,"line":584},3,[514,586,587],{"class":576},"    scheme",[514,589,380],{"class":543},[514,591,592],{"class":557}," \"",[514,594,595],{"class":561},"postgres",[514,597,558],{"class":557},[514,599,600],{"class":543},",\n",[514,602,604,607,609,612,614,617,619,622],{"class":516,"line":603},4,[514,605,606],{"class":576},"    host",[514,608,380],{"class":543},[514,610,611],{"class":539}," secret",[514,613,338],{"class":543},[514,615,616],{"class":539},"DatabaseHost",[514,618,338],{"class":543},[514,620,621],{"class":539},"value",[514,623,600],{"class":543},[514,625,627,630,632,634,636,639,641,643,645,648,651],{"class":516,"line":626},5,[514,628,629],{"class":576},"    port",[514,631,380],{"class":543},[514,633,611],{"class":539},[514,635,338],{"class":543},[514,637,638],{"class":539},"DatabasePort",[514,640,338],{"class":543},[514,642,621],{"class":539},[514,644,338],{"class":543},[514,646,647],{"class":551},"apply",[514,649,650],{"class":539},"(Number)",[514,652,600],{"class":543},[514,654,656,659,661,663,665,668,670,672],{"class":516,"line":655},6,[514,657,658],{"class":576},"    database",[514,660,380],{"class":543},[514,662,611],{"class":539},[514,664,338],{"class":543},[514,666,667],{"class":539},"DatabaseName",[514,669,338],{"class":543},[514,671,621],{"class":539},[514,673,600],{"class":543},[514,675,677,680,682,684,686,689,691,693],{"class":516,"line":676},7,[514,678,679],{"class":576},"    user",[514,681,380],{"class":543},[514,683,611],{"class":539},[514,685,338],{"class":543},[514,687,688],{"class":539},"DatabaseUser",[514,690,338],{"class":543},[514,692,621],{"class":539},[514,694,600],{"class":543},[514,696,698,701,703,705,707,710,712,714],{"class":516,"line":697},8,[514,699,700],{"class":576},"    password",[514,702,380],{"class":543},[514,704,611],{"class":539},[514,706,338],{"class":543},[514,708,709],{"class":539},"DatabasePassword",[514,711,338],{"class":543},[514,713,621],{"class":539},[514,715,600],{"class":543},[514,717,719],{"class":516,"line":718},9,[514,720,721],{"class":543},"  },\n",[514,723,725,728,731],{"class":516,"line":724},10,[514,726,727],{"class":543},"}",[514,729,730],{"class":539},")",[514,732,733],{"class":543},";\n",[317,735,736],{},"Hyperdrive pools and caches the connection; the app always speaks plain Postgres wire protocol, so the origin is swappable — Supabase, RDS, Neon, self-hosted — without touching application code.",[738,739,740],"blockquote",{},[317,741,742,745,746,751,752,756],{},[321,743,744],{},"Supabase connection mode:"," Cloudflare's official Hyperdrive documentation ",[326,747,750],{"href":748,"rel":749},"https:\u002F\u002Fdevelopers.cloudflare.com\u002Fhyperdrive\u002Fexamples\u002Fconnect-to-postgres\u002Fpostgres-database-providers\u002Fsupabase\u002F",[330],"explicitly recommends using Supabase's Direct connection"," instead of pooled connection strings, because Hyperdrive already performs connection pooling at Cloudflare's edge (avoiding double-pooling). See ",[326,753,755],{"href":754},"\u002Fdocs\u002Fsaas-starter-kit\u002Fgetting-started\u002Fenvironment-variables#use-the-direct-connection-not-the-session-pooler","Environment Variables — Use the Direct connection"," for host\u002Fuser formatting and local migration IPv6 notes.",[345,758,760],{"id":759},"_3-database-connection","3. Database Connection",[317,762,763,766,767,770,771,774],{},[353,764,765],{},"useDB()"," (",[353,768,769],{},"apps\u002Fapp\u002Fserver\u002Futils\u002Fdb.ts",") builds a ",[353,772,773],{},"drizzle-orm\u002Fpostgres-js"," client from the Hyperdrive binding, scoped to the current request and never cached globally:",[505,776,778],{"className":507,"code":777,"filename":769,"language":509,"meta":510,"style":510},"function createDB() {\n  const sql = postgres(binding.connectionString, { max: 5, fetch_types: false });\n  return drizzle(sql, { schema });\n}\n\nexport function useDB() {\n  const event = useEvent();\n  const ctx = event.context as { _db?: DB };\n  if (!ctx._db) ctx._db = createDB();\n  return ctx._db;\n}\n",[353,779,780,793,848,874,879,884,898,914,949,984,996],{"__ignoreMap":510},[514,781,782,785,788,791],{"class":516,"line":517},[514,783,784],{"class":524},"function",[514,786,787],{"class":551}," createDB",[514,789,790],{"class":543},"()",[514,792,570],{"class":543},[514,794,795,798,801,803,806,808,811,813,816,818,821,824,826,830,832,835,837,841,844,846],{"class":516,"line":573},[514,796,797],{"class":524},"  const",[514,799,800],{"class":528}," sql",[514,802,533],{"class":532},[514,804,805],{"class":551}," postgres",[514,807,554],{"class":576},[514,809,810],{"class":539},"binding",[514,812,338],{"class":543},[514,814,815],{"class":539},"connectionString",[514,817,567],{"class":543},[514,819,820],{"class":543}," {",[514,822,823],{"class":576}," max",[514,825,380],{"class":543},[514,827,829],{"class":828},"sYThS"," 5",[514,831,567],{"class":543},[514,833,834],{"class":576}," fetch_types",[514,836,380],{"class":543},[514,838,840],{"class":839},"s8HiA"," false",[514,842,843],{"class":543}," }",[514,845,730],{"class":576},[514,847,733],{"class":543},[514,849,850,853,856,858,861,863,865,868,870,872],{"class":516,"line":584},[514,851,852],{"class":520},"  return",[514,854,855],{"class":551}," drizzle",[514,857,554],{"class":576},[514,859,860],{"class":539},"sql",[514,862,567],{"class":543},[514,864,820],{"class":543},[514,866,867],{"class":539}," schema",[514,869,843],{"class":543},[514,871,730],{"class":576},[514,873,733],{"class":543},[514,875,876],{"class":516,"line":603},[514,877,878],{"class":543},"}\n",[514,880,881],{"class":516,"line":626},[514,882,883],{"emptyLinePlaceholder":22},"\n",[514,885,886,888,891,894,896],{"class":516,"line":655},[514,887,521],{"class":520},[514,889,890],{"class":524}," function",[514,892,893],{"class":551}," useDB",[514,895,790],{"class":543},[514,897,570],{"class":543},[514,899,900,902,905,907,910,912],{"class":516,"line":676},[514,901,797],{"class":524},[514,903,904],{"class":528}," event",[514,906,533],{"class":532},[514,908,909],{"class":551}," useEvent",[514,911,790],{"class":576},[514,913,733],{"class":543},[514,915,916,918,921,923,925,927,930,933,935,939,942,946],{"class":516,"line":697},[514,917,797],{"class":524},[514,919,920],{"class":528}," ctx",[514,922,533],{"class":532},[514,924,904],{"class":539},[514,926,338],{"class":543},[514,928,929],{"class":539},"context",[514,931,932],{"class":520}," as",[514,934,820],{"class":543},[514,936,938],{"class":937},"sIDdj"," _db",[514,940,941],{"class":532},"?:",[514,943,945],{"class":944},"sKvfc"," DB",[514,947,948],{"class":543}," };\n",[514,950,951,954,956,959,962,964,967,970,972,974,976,978,980,982],{"class":516,"line":718},[514,952,953],{"class":520},"  if",[514,955,766],{"class":576},[514,957,958],{"class":532},"!",[514,960,961],{"class":539},"ctx",[514,963,338],{"class":543},[514,965,966],{"class":539},"_db",[514,968,969],{"class":576},") ",[514,971,961],{"class":539},[514,973,338],{"class":543},[514,975,966],{"class":539},[514,977,533],{"class":532},[514,979,787],{"class":551},[514,981,790],{"class":576},[514,983,733],{"class":543},[514,985,986,988,990,992,994],{"class":516,"line":724},[514,987,852],{"class":520},[514,989,920],{"class":539},[514,991,338],{"class":543},[514,993,966],{"class":539},[514,995,733],{"class":543},[514,997,999],{"class":516,"line":998},11,[514,1000,878],{"class":543},[317,1002,1003,1004,1007],{},"This matters because a ",[353,1005,1006],{},"postgres.js"," client is an I\u002FO object, and Workers tears down a request's I\u002FO once that request ends — reusing a client built during one request inside a later one fails intermittently (Cloudflare documents this under \"Cannot perform I\u002FO on behalf of a different request\"). Hyperdrive's own connection pooling already removes the cost of opening a fresh client per request, so there's no benefit to caching one across requests, only risk.",[340,1009,1011],{"id":1010},"database-usage","Database Usage",[317,1013,1014],{},"Example query using Drizzle's relational API:",[505,1016,1018],{"className":507,"code":1017,"language":509,"meta":510,"style":510},"const team = await useDB().query.teams.findFirst({\n  where: or(eq(teams.id, teamIdentifier), eq(teams.slug, teamIdentifier)),\n  with: {\n    memberships: {\n      with: {\n        user: { columns: { id: true, name: true, email: true, image: true } },\n        role: { columns: { id: true, name: true } },\n      },\n    },\n  },\n});\n",[353,1019,1020,1056,1103,1112,1121,1130,1186,1219,1224,1229,1233],{"__ignoreMap":510},[514,1021,1022,1025,1028,1030,1033,1035,1037,1039,1042,1044,1046,1048,1051,1053],{"class":516,"line":517},[514,1023,1024],{"class":524},"const",[514,1026,1027],{"class":528}," team",[514,1029,533],{"class":532},[514,1031,1032],{"class":520}," await",[514,1034,893],{"class":551},[514,1036,790],{"class":539},[514,1038,338],{"class":543},[514,1040,1041],{"class":539},"query",[514,1043,338],{"class":543},[514,1045,442],{"class":539},[514,1047,338],{"class":543},[514,1049,1050],{"class":551},"findFirst",[514,1052,554],{"class":539},[514,1054,1055],{"class":543},"{\n",[514,1057,1058,1061,1063,1066,1068,1071,1074,1076,1079,1081,1084,1086,1089,1091,1093,1096,1098,1101],{"class":516,"line":573},[514,1059,1060],{"class":576},"  where",[514,1062,380],{"class":543},[514,1064,1065],{"class":551}," or",[514,1067,554],{"class":539},[514,1069,1070],{"class":551},"eq",[514,1072,1073],{"class":539},"(teams",[514,1075,338],{"class":543},[514,1077,1078],{"class":539},"id",[514,1080,567],{"class":543},[514,1082,1083],{"class":539}," teamIdentifier)",[514,1085,567],{"class":543},[514,1087,1088],{"class":551}," eq",[514,1090,1073],{"class":539},[514,1092,338],{"class":543},[514,1094,1095],{"class":539},"slug",[514,1097,567],{"class":543},[514,1099,1100],{"class":539}," teamIdentifier))",[514,1102,600],{"class":543},[514,1104,1105,1108,1110],{"class":516,"line":584},[514,1106,1107],{"class":576},"  with",[514,1109,380],{"class":543},[514,1111,570],{"class":543},[514,1113,1114,1117,1119],{"class":516,"line":603},[514,1115,1116],{"class":576},"    memberships",[514,1118,380],{"class":543},[514,1120,570],{"class":543},[514,1122,1123,1126,1128],{"class":516,"line":626},[514,1124,1125],{"class":576},"      with",[514,1127,380],{"class":543},[514,1129,570],{"class":543},[514,1131,1132,1135,1137,1139,1142,1144,1146,1149,1151,1154,1156,1159,1161,1163,1165,1168,1170,1172,1174,1177,1179,1181,1183],{"class":516,"line":655},[514,1133,1134],{"class":576},"        user",[514,1136,380],{"class":543},[514,1138,820],{"class":543},[514,1140,1141],{"class":576}," columns",[514,1143,380],{"class":543},[514,1145,820],{"class":543},[514,1147,1148],{"class":576}," id",[514,1150,380],{"class":543},[514,1152,1153],{"class":839}," true",[514,1155,567],{"class":543},[514,1157,1158],{"class":576}," name",[514,1160,380],{"class":543},[514,1162,1153],{"class":839},[514,1164,567],{"class":543},[514,1166,1167],{"class":576}," email",[514,1169,380],{"class":543},[514,1171,1153],{"class":839},[514,1173,567],{"class":543},[514,1175,1176],{"class":576}," image",[514,1178,380],{"class":543},[514,1180,1153],{"class":839},[514,1182,843],{"class":543},[514,1184,1185],{"class":543}," },\n",[514,1187,1188,1191,1193,1195,1197,1199,1201,1203,1205,1207,1209,1211,1213,1215,1217],{"class":516,"line":676},[514,1189,1190],{"class":576},"        role",[514,1192,380],{"class":543},[514,1194,820],{"class":543},[514,1196,1141],{"class":576},[514,1198,380],{"class":543},[514,1200,820],{"class":543},[514,1202,1148],{"class":576},[514,1204,380],{"class":543},[514,1206,1153],{"class":839},[514,1208,567],{"class":543},[514,1210,1158],{"class":576},[514,1212,380],{"class":543},[514,1214,1153],{"class":839},[514,1216,843],{"class":543},[514,1218,1185],{"class":543},[514,1220,1221],{"class":516,"line":697},[514,1222,1223],{"class":543},"      },\n",[514,1225,1226],{"class":516,"line":718},[514,1227,1228],{"class":543},"    },\n",[514,1230,1231],{"class":516,"line":724},[514,1232,721],{"class":543},[514,1234,1235,1237,1239],{"class":516,"line":998},[514,1236,727],{"class":543},[514,1238,730],{"class":539},[514,1240,733],{"class":543},[340,1242,1244],{"id":1243},"database-migrations","Database Migrations",[317,1246,1247,1248,1250,1251,1253,1254,1257,1258,766,1261,1264,1265,380],{},"Migrations are generated with Drizzle Kit from ",[353,1249,391],{}," and every layer's ",[353,1252,359],{}," — the exact list ",[353,1255,1256],{},"apps\u002Ffunctions\u002Fdrizzle.config.ts"," declares under ",[353,1259,1260],{},"schema: [...]",[353,1262,1263],{},"dialect: \"postgresql\"",") — and committed to ",[353,1266,1267],{},"apps\u002Ffunctions\u002Fsrc\u002Fdatabase\u002Fmigrations\u002F",[505,1269,1273],{"className":1270,"code":1271,"language":1272,"meta":510,"style":510},"language-bash shiki shiki-themes material-theme-lighter github-light github-dark monokai","cd apps\u002Ffunctions\nDATABASE_HOST=... DATABASE_PORT=5432 DATABASE_USER=... DATABASE_PASSWORD=... DATABASE_NAME=... \\\n  bunx drizzle-kit generate\n","bash",[353,1274,1275,1284,1328],{"__ignoreMap":510},[514,1276,1277,1281],{"class":516,"line":517},[514,1278,1280],{"class":1279},"sMLJd","cd",[514,1282,1283],{"class":561}," apps\u002Ffunctions\n",[514,1285,1286,1289,1292,1295,1298,1300,1303,1306,1308,1310,1313,1315,1317,1320,1322,1324],{"class":516,"line":573},[514,1287,1288],{"class":539},"DATABASE_HOST",[514,1290,1291],{"class":532},"=",[514,1293,1294],{"class":561},"...",[514,1296,1297],{"class":539}," DATABASE_PORT",[514,1299,1291],{"class":532},[514,1301,1302],{"class":561},"5432",[514,1304,1305],{"class":539}," DATABASE_USER",[514,1307,1291],{"class":532},[514,1309,1294],{"class":561},[514,1311,1312],{"class":539}," DATABASE_PASSWORD",[514,1314,1291],{"class":532},[514,1316,1294],{"class":561},[514,1318,1319],{"class":539}," DATABASE_NAME",[514,1321,1291],{"class":532},[514,1323,1294],{"class":561},[514,1325,1327],{"class":1326},"sR7ES"," \\\n",[514,1329,1330,1333,1336],{"class":516,"line":584},[514,1331,1332],{"class":561},"  bunx",[514,1334,1335],{"class":561}," drizzle-kit",[514,1337,1338],{"class":561}," generate\n",[317,1340,1341,1342,1345],{},"Review the generated SQL before committing — Drizzle Kit diffs the schema files above against the last snapshot in ",[353,1343,1344],{},"migrations\u002Fmeta\u002F",", but can't know intent (e.g. a column rename vs. a drop-and-add), so double-check anything destructive.",[345,1347,1349],{"id":1348},"deployment","Deployment",[317,1351,1352,1353,1356,1357,1360,1361,1364,1365,472,1367,472,1369,472,1371,472,1373,1375],{},"Every deploy of the ",[353,1354,1355],{},"App"," Worker runs ",[353,1358,1359],{},"drizzle-kit migrate"," automatically, right after the Worker is built (see ",[353,1362,1363],{},"infra\u002Futils\u002Fnuxt.ts","), using the ",[353,1366,616],{},[353,1368,638],{},[353,1370,688],{},[353,1372,709],{},[353,1374,667],{}," secrets. There's no separate migration step to run by hand.",[345,1377,1379],{"id":1378},"rollback-strategy","Rollback strategy",[317,1381,1382,1383,1386],{},"Drizzle Kit doesn't generate down-migrations — Postgres migrations here are append-only. The short version (full detail in ",[353,1384,1385],{},"apps\u002Ffunctions\u002Fsrc\u002Fdatabase\u002FMIGRATIONS.md","):",[382,1388,1389,1395,1405,1408],{},[385,1390,1391,1394],{},[321,1392,1393],{},"Additive changes"," (new table, nullable column, index) are safe to leave in place even if the deploy is rolled back.",[385,1396,1397,1400,1401,1404],{},[321,1398,1399],{},"Destructive changes"," (drop column\u002Ftable, new ",[353,1402,1403],{},"NOT NULL",", type changes) ship in their own migration, deployed only after the application code that depended on the old shape has been live a while — the standard expand\u002Fcontract pattern.",[385,1406,1407],{},"A bad migration that reaches production gets undone with a new forward migration, not a revert.",[385,1409,1410],{},"Take a manual backup before any migration that drops or alters data-bearing columns.",[1412,1413,1414],"style",{},"html pre.shiki code .sRxSC, html code.shiki .sRxSC{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#D73A49;--shiki-default-font-style:inherit;--shiki-dark:#F97583;--shiki-dark-font-style:inherit;--shiki-sepia:#F92672;--shiki-sepia-font-style:inherit}html pre.shiki code .srJo8, html code.shiki .srJo8{--shiki-light:#9C3EDA;--shiki-light-font-style:inherit;--shiki-default:#D73A49;--shiki-default-font-style:inherit;--shiki-dark:#F97583;--shiki-dark-font-style:inherit;--shiki-sepia:#66D9EF;--shiki-sepia-font-style:italic}html pre.shiki code .s91G_, html code.shiki .s91G_{--shiki-light:#90A4AE;--shiki-default:#005CC5;--shiki-dark:#79B8FF;--shiki-sepia:#F8F8F2}html pre.shiki code .sGXK2, html code.shiki .sGXK2{--shiki-light:#39ADB5;--shiki-default:#D73A49;--shiki-dark:#F97583;--shiki-sepia:#F92672}html pre.shiki code .ss--_, html code.shiki .ss--_{--shiki-light:#90A4AE;--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#F8F8F2}html pre.shiki code .swvn1, html code.shiki .swvn1{--shiki-light:#39ADB5;--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#F8F8F2}html pre.shiki code .sD0ED, html code.shiki .sD0ED{--shiki-light:#6182B8;--shiki-default:#6F42C1;--shiki-dark:#B392F0;--shiki-sepia:#A6E22E}html pre.shiki code .siCPE, html code.shiki .siCPE{--shiki-light:#39ADB5;--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#E6DB74}html pre.shiki code .sLACW, html code.shiki .sLACW{--shiki-light:#91B859;--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#E6DB74}html pre.shiki code .squCx, html code.shiki .squCx{--shiki-light:#E53935;--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#F8F8F2}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html pre.shiki code .sYThS, html code.shiki .sYThS{--shiki-light:#F76D47;--shiki-default:#005CC5;--shiki-dark:#79B8FF;--shiki-sepia:#AE81FF}html pre.shiki code .s8HiA, html code.shiki .s8HiA{--shiki-light:#FF5370;--shiki-default:#005CC5;--shiki-dark:#79B8FF;--shiki-sepia:#AE81FF}html pre.shiki code .sIDdj, html code.shiki .sIDdj{--shiki-light:#E53935;--shiki-default:#E36209;--shiki-dark:#FFAB70;--shiki-sepia:#F8F8F2}html pre.shiki code .sKvfc, html code.shiki .sKvfc{--shiki-light:#E2931D;--shiki-light-text-decoration:inherit;--shiki-default:#6F42C1;--shiki-default-text-decoration:inherit;--shiki-dark:#B392F0;--shiki-dark-text-decoration:inherit;--shiki-sepia:#A6E22E;--shiki-sepia-text-decoration:underline}html pre.shiki code .sMLJd, html code.shiki .sMLJd{--shiki-light:#6182B8;--shiki-default:#005CC5;--shiki-dark:#79B8FF;--shiki-sepia:#66D9EF}html pre.shiki code .sR7ES, html code.shiki .sR7ES{--shiki-light:#E2931D;--shiki-default:#6F42C1;--shiki-dark:#B392F0;--shiki-sepia:#A6E22E}",{"title":510,"searchDepth":573,"depth":573,"links":1416},[1417,1422,1423],{"id":342,"depth":573,"text":343,"children":1418},[1419,1420,1421],{"id":347,"depth":584,"text":348},{"id":496,"depth":584,"text":497},{"id":759,"depth":584,"text":760},{"id":1010,"depth":573,"text":1011},{"id":1243,"depth":573,"text":1244,"children":1424},[1425,1426],{"id":1348,"depth":584,"text":1349},{"id":1378,"depth":584,"text":1379},"Postgres, reached from Cloudflare Workers through Hyperdrive, with Drizzle ORM.","md",{},{"title":98,"description":1427},"OLG_kxI3e93NOICaGJZRKHhqddKBbb_tnyWoJb5pLBg",1790707485288]