[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"docs-navigation":3,"docs-page-deployment":166},[4,38,57,65,114,138,157],{"id":5,"group":6,"pages":7},"get-started","Get started",[8,13,18,23,28,33],{"path":9,"title":10,"description":11,"navTitle":10,"group":6,"order":12},"\u002Fdocs\u002Fgetting-started","Getting started","Create your app from the kit, run it locally with no credentials, make yourself superadmin and find where your own code goes.",1,{"path":14,"title":15,"description":16,"navTitle":15,"group":6,"order":17},"\u002Fdocs\u002Flayout","Project structure","Where code lives by where it runs, and which server\u002F directories turn a new file into a route, a job or a provider.",2,{"path":19,"title":20,"description":21,"navTitle":20,"group":6,"order":22},"\u002Fdocs\u002Fconfiguration","Configuration","starter.config.ts switches each part of the kit and its providers, environment variables hold the secrets: every key and what it changes.",3,{"path":24,"title":25,"description":26,"navTitle":25,"group":6,"order":27},"\u002Fdocs\u002Ftemplate","Updating from the kit","Keep the kit as a git remote and take each update as one commit you review, with pnpm template:update, then resolve what conflicts.",4,{"path":29,"title":30,"description":31,"navTitle":30,"group":6,"order":32},"\u002Fdocs\u002Fdeployment","Deployment","Ship the app as one Cloudflare Worker: create the resources, set the secrets, deploy main from GitHub Actions or your machine.",5,{"path":34,"title":35,"description":36,"navTitle":35,"group":6,"order":37},"\u002Fdocs\u002Fmcp","MCP server","Connect Claude Code, Cursor or VS Code to https:\u002F\u002Fnuxter.app\u002Fmcp, so your agent searches and reads these docs and their design notes.",6,{"id":39,"group":40,"pages":41},"accounts","Accounts",[42,47,52],{"path":43,"title":44,"description":45,"navTitle":44,"group":40,"order":46},"\u002Fdocs\u002Fauth","Auth","Pick the sign-in methods, add an OAuth provider, protect a route, and handle sessions, second factors and account deletion.",7,{"path":48,"title":49,"description":50,"navTitle":49,"group":40,"order":51},"\u002Fdocs\u002Ftenancy","Organizations","Scope routes and pages to an organization, and work with its members, roles, seats, invitations and a second level such as branches.",8,{"path":53,"title":54,"description":55,"navTitle":54,"group":40,"order":56},"\u002Fdocs\u002Fpermissions","Roles and permissions","Routes check permissions that an account’s role or its organization’s plan grants: checking one, adding a role, and the staff tools.",9,{"id":58,"group":59,"pages":60},"payments","Payments",[61],{"path":62,"title":59,"description":63,"navTitle":59,"group":59,"order":64},"\u002Fdocs\u002Fpayments","Sell plans through Stripe, Polar, Lemon Squeezy or Creem: define plans, connect a provider, gate features, trials, coupons and refunds.",10,{"id":66,"group":67,"pages":68},"building-blocks","Building blocks",[69,74,79,84,89,94,99,104,109],{"path":70,"title":71,"description":72,"navTitle":71,"group":67,"order":73},"\u002Fdocs\u002Fdatabase","Database","Postgres through Drizzle, on Hyperdrive in production and PGlite locally: add a table, query, transactions, migrations and seed data.",11,{"path":75,"title":76,"description":77,"navTitle":76,"group":67,"order":78},"\u002Fdocs\u002Fbackground-jobs","Background jobs","Run work after the response on Cloudflare Queues with retries: add and enqueue a job, run jobs locally, and schedule cron tasks.",12,{"path":80,"title":81,"description":82,"navTitle":81,"group":67,"order":83},"\u002Fdocs\u002Fevents","Events","React to what the kit does by adding a file to server\u002Flisteners\u002F, fire events of your own, and look up every event’s payload.",13,{"path":85,"title":86,"description":87,"navTitle":86,"group":67,"order":88},"\u002Fdocs\u002Femail","Email","Set up sending, send your own mail with deliverEmail, change the templates and wording, handle unsubscribes and add a provider.",14,{"path":90,"title":91,"description":92,"navTitle":91,"group":67,"order":93},"\u002Fdocs\u002Fcontact","Contact","A \u002Fcontact page that mails you a message or shows your address after a Turnstile check: choose the channels, set the address, know the limits.",15,{"path":95,"title":96,"description":97,"navTitle":96,"group":67,"order":98},"\u002Fdocs\u002Fnotifications","Notifications","The dashboard bell: add a notification type, send one with notify(), and how categories, opt-outs and retention apply.",16,{"path":100,"title":101,"description":102,"navTitle":101,"group":67,"order":103},"\u002Fdocs\u002Fstorage","Storage","Uploads in R2 with one policy per purpose: add a purpose, accept an upload, replace or delete a file, and serve it to its owner.",17,{"path":105,"title":106,"description":107,"navTitle":106,"group":67,"order":108},"\u002Fdocs\u002Ffeature-flags","Feature flags","Declare typed flags in starter.config.ts, read them with flag() or useFlag(), roll them out by rule or percentage, and add a provider.",18,{"path":110,"title":111,"description":112,"navTitle":111,"group":67,"order":113},"\u002Fdocs\u002Fai","AI","Call models from server code by role: text, images, speech, transcription, embeddings and video, with provider fallbacks.",19,{"id":115,"group":116,"pages":117},"web","Web",[118,123,128,133],{"path":119,"title":120,"description":121,"navTitle":120,"group":116,"order":122},"\u002Fdocs\u002Fsecurity-headers","Security headers","The Content-Security-Policy and hardening headers on every response: allow another origin, keep tokens out of Referer, accept cross-site posts.",20,{"path":124,"title":125,"description":126,"navTitle":125,"group":116,"order":127},"\u002Fdocs\u002Frate-limiting","Rate limiting","Cap how often a caller hits a route with requireRateLimit, count only failures, space out sends, and protect the zone.",21,{"path":129,"title":130,"description":131,"navTitle":130,"group":116,"order":132},"\u002Fdocs\u002Fanalytics","Analytics","Product analytics and ad conversions sent from the server with no browser script: add an event, connect a provider, and handle consent.",22,{"path":134,"title":135,"description":136,"navTitle":135,"group":116,"order":137},"\u002Fdocs\u002Fobservability","Observability","Find a failed request by its id, send faults to Sentry or Better Stack, report an error you caught, and change the error page.",23,{"id":139,"group":140,"pages":141},"content-and-look","Content and look",[142,147,152],{"path":143,"title":144,"description":145,"navTitle":144,"group":140,"order":146},"\u002Fdocs\u002Fblog","Blog","Markdown posts with an RSS feed per language, and workflows that draft posts with an AI model and open each one as a pull request.",24,{"path":148,"title":149,"description":150,"navTitle":149,"group":140,"order":151},"\u002Fdocs\u002Flanguages","Languages","Add or remove a language, write copy in the locale files, and write code, refusals and links that follow the reader’s language.",25,{"path":153,"title":154,"description":155,"navTitle":154,"group":140,"order":156},"\u002Fdocs\u002Ftheme","Theme","Change the brand colour and font, and build dashboard sections, list pages, loading placeholders and charts that match the kit.",26,{"id":158,"group":159,"pages":160},"quality","Quality",[161],{"path":162,"title":163,"description":164,"navTitle":163,"group":159,"order":165},"\u002Fdocs\u002Ftesting","Testing","Unit tests in Node, component tests in a real Nuxt app and Playwright end to end: test a route or a component, and what CI runs.",27,{"page":167,"surround":1445,"linksToKit":1448},{"path":29,"title":30,"description":31,"navTitle":30,"group":6,"seo":168,"body":170},{"title":169,"description":31},"Deploy a Nuxt SaaS to Cloudflare Workers",{"type":171,"value":172,"toc":1434},"minimark",[173,177,190,195,218,270,273,277,286,445,471,481,501,505,508,595,603,606,641,665,803,825,829,843,854,863,866,881,887,891,914,950,954,957,960,966,969,981,1215,1245,1248,1374,1378,1430],[174,175,30],"h1",{"id":176},"deployment",[178,179,180,181,185,186,189],"p",{},"The app ships as one Cloudflare Worker. A GitHub Actions workflow deploys ",[182,183,184],"code",{},"main","\nafter CI passes, and ",[182,187,188],{},"pnpm deploy:cf"," does the same from your machine. You work\nhere once to create the Cloudflare resources and set the secrets, and again when\nyou add a binding or a domain.",[191,192,194],"h2",{"id":193},"how-it-works","How it works",[178,196,197,207,208,211,212,214,215,217],{},[198,199,204],"a",{"href":200,"rel":201,"title":203},"https:\u002F\u002Fgithub.com\u002Fnuxter-app\u002Fkit\u002Fblob\u002Fmain\u002F.github\u002Fworkflows\u002Fdeploy.yml",[202],"nofollow","Licensees only",[182,205,206],{},"deploy.yml"," runs when the ",[182,209,210],{},"ci"," workflow\nsucceeds on ",[182,213,184],{},", or when you start it by hand on ",[182,216,184],{},".",[219,220,221,228,246,258,264],"ol",{},[222,223,224,225,227],"li",{},"It checks out the commit CI tested. If ",[182,226,184],{}," has moved past that commit, it\nwrites a line in the run summary and deploys nothing.",[222,229,230,237,238,241,242,245],{},[198,231,234],{"href":232,"rel":233,"title":203},"https:\u002F\u002Fgithub.com\u002Fnuxter-app\u002Fkit\u002Fblob\u002Fmain\u002Fscripts\u002Fdb\u002Fdeploy-preflight.mjs",[202],[182,235,236],{},"scripts\u002Fdb\u002Fdeploy-preflight.mjs","\nstops the run while ",[182,239,240],{},"wrangler.jsonc"," still holds an all-zeros placeholder id\nor ",[182,243,244],{},"DATABASE_URL"," is empty, and while the Hyperdrive has query caching on.",[222,247,248,251,252,254,255,217],{},[182,249,250],{},"pnpm build"," writes the Worker and merges ",[182,253,240],{}," into\n",[182,256,257],{},".output\u002Fserver\u002Fwrangler.json",[222,259,260,261,263],{},"The migrations run over ",[182,262,244],{},", straight to the database.",[222,265,266,269],{},[182,267,268],{},"wrangler deploy"," uploads the Worker with the bindings from that file.",[178,271,272],{},"Any failed step ends the run. If the migration fails, the old Worker stays live\non the old schema.",[191,274,276],{"id":275},"create-the-cloudflare-resources","Create the Cloudflare resources",[178,278,279,280,282,283,285],{},"Create a Postgres database first, with any provider Hyperdrive can reach\n(",[198,281,71],{"href":70}," lists what it must allow). Then create everything\n",[182,284,240],{}," names:",[287,288,293],"pre",{"className":289,"code":290,"language":291,"meta":292,"style":292},"language-bash shiki shiki-themes github-light github-light github-dark","pnpm exec wrangler hyperdrive create nuxter --caching-disabled --connection-string=\"postgres:\u002F\u002FUSER:PASSWORD@HOST:5432\u002FDATABASE\"\npnpm exec wrangler d1 create nuxter\npnpm exec wrangler kv namespace create CACHE\npnpm exec wrangler r2 bucket create nuxter-avatars\npnpm exec wrangler queues create nuxter-jobs\npnpm exec wrangler queues create nuxter-jobs-dlq\npnpm exec wrangler email sending enable \u003Cyour-domain>\n","bash","",[182,294,295,329,345,364,383,399,414],{"__ignoreMap":292},[296,297,299,303,307,310,313,316,319,323,326],"span",{"class":298,"line":12},"line",[296,300,302],{"class":301},"snPdu","pnpm",[296,304,306],{"class":305},"sIIMD"," exec",[296,308,309],{"class":305}," wrangler",[296,311,312],{"class":305}," hyperdrive",[296,314,315],{"class":305}," create",[296,317,318],{"class":305}," nuxter",[296,320,322],{"class":321},"sBjJW"," --caching-disabled",[296,324,325],{"class":321}," --connection-string=",[296,327,328],{"class":305},"\"postgres:\u002F\u002FUSER:PASSWORD@HOST:5432\u002FDATABASE\"\n",[296,330,331,333,335,337,340,342],{"class":298,"line":17},[296,332,302],{"class":301},[296,334,306],{"class":305},[296,336,309],{"class":305},[296,338,339],{"class":305}," d1",[296,341,315],{"class":305},[296,343,344],{"class":305}," nuxter\n",[296,346,347,349,351,353,356,359,361],{"class":298,"line":22},[296,348,302],{"class":301},[296,350,306],{"class":305},[296,352,309],{"class":305},[296,354,355],{"class":305}," kv",[296,357,358],{"class":305}," namespace",[296,360,315],{"class":305},[296,362,363],{"class":305}," CACHE\n",[296,365,366,368,370,372,375,378,380],{"class":298,"line":27},[296,367,302],{"class":301},[296,369,306],{"class":305},[296,371,309],{"class":305},[296,373,374],{"class":305}," r2",[296,376,377],{"class":305}," bucket",[296,379,315],{"class":305},[296,381,382],{"class":305}," nuxter-avatars\n",[296,384,385,387,389,391,394,396],{"class":298,"line":32},[296,386,302],{"class":301},[296,388,306],{"class":305},[296,390,309],{"class":305},[296,392,393],{"class":305}," queues",[296,395,315],{"class":305},[296,397,398],{"class":305}," nuxter-jobs\n",[296,400,401,403,405,407,409,411],{"class":298,"line":37},[296,402,302],{"class":301},[296,404,306],{"class":305},[296,406,309],{"class":305},[296,408,393],{"class":305},[296,410,315],{"class":305},[296,412,413],{"class":305}," nuxter-jobs-dlq\n",[296,415,416,418,420,422,425,428,431,435,438,442],{"class":298,"line":46},[296,417,302],{"class":301},[296,419,306],{"class":305},[296,421,309],{"class":305},[296,423,424],{"class":305}," email",[296,426,427],{"class":305}," sending",[296,429,430],{"class":305}," enable",[296,432,434],{"class":433},"s8jYJ"," \u003C",[296,436,437],{"class":305},"your-domai",[296,439,441],{"class":440},"sxrX7","n",[296,443,444],{"class":433},">\n",[178,446,447,448,451,452,455,456,459,460,463,464,467,468,470],{},"Paste the ids they print over the placeholders under ",[182,449,450],{},"hyperdrive",",\n",[182,453,454],{},"d1_databases"," and ",[182,457,458],{},"kv_namespaces",", then run ",[182,461,462],{},"pnpm cf-typegen",". The D1 database\nholds Nuxt Content's pages and nothing else. The last command onboards the\ndomain your mail is sent from, for the ",[182,465,466],{},"cloudflare"," mail provider\n(",[198,469,86],{"href":85},").",[178,472,473,474,477,478,480],{},"The ",[182,475,476],{},"create"," commands append a new binding to ",[182,479,240],{}," instead of\nfilling in the one already there, and some Wrangler versions reindent the file\nwith tabs. Read the diff, delete the stanza they added, and paste the id into\nthe existing one.",[178,482,483,484,486,487,489,490,493,494,455,497,500],{},"Type the connection string into the Hyperdrive command rather than exporting\n",[182,485,244],{},". A ",[182,488,244],{}," exported in your shell outranks ",[182,491,492],{},".env",", so\n",[182,495,496],{},"pnpm dev",[182,498,499],{},"pnpm db:migrate"," run from that shell would write to production.",[191,502,504],{"id":503},"set-secrets-and-variables","Set secrets and variables",[178,506,507],{},"In GitHub, under Settings > Secrets and variables > Actions:",[509,510,511,527],"table",{},[512,513,514],"thead",{},[515,516,517,521,524],"tr",{},[518,519,520],"th",{},"Name",[518,522,523],{},"Kind",[518,525,526],{},"Value",[528,529,530,544,556,567,583],"tbody",{},[515,531,532,538,541],{},[533,534,535],"td",{},[182,536,537],{},"CLOUDFLARE_API_TOKEN",[533,539,540],{},"secret",[533,542,543],{},"A token from the Edit Cloudflare Workers template, plus Account > Queues > Edit and Account > Hyperdrive > Read",[515,545,546,551,553],{},[533,547,548],{},[182,549,550],{},"CLOUDFLARE_ACCOUNT_ID",[533,552,540],{},[533,554,555],{},"Your Cloudflare account id",[515,557,558,562,564],{},[533,559,560],{},[182,561,244],{},[533,563,540],{},[533,565,566],{},"The database's own connection string, the one the Hyperdrive was created with. The migrations can't go through Hyperdrive",[515,568,569,574,577],{},[533,570,571],{},[182,572,573],{},"NUXT_SITE_URL",[533,575,576],{},"variable",[533,578,579,582],{},[182,580,581],{},"https:\u002F\u002Fyour-domain",". Baked into the build and every mailed link",[515,584,585,590,592],{},[533,586,587],{},[182,588,589],{},"NUXT_PUBLIC_TURNSTILE_SITE_KEY",[533,591,576],{},[533,593,594],{},"From a Turnstile widget created for your hostname",[178,596,597,598,600,601,470],{},"The blog workflows use the same ",[182,599,537],{},", so if you run them the\ntoken also needs their permissions (",[198,602,144],{"href":143},[178,604,605],{},"Everything else goes on the Worker as a secret. Secrets survive deploys, so you\nset each one once:",[287,607,609],{"className":289,"code":608,"language":291,"meta":292,"style":292},"pnpm exec wrangler secret put NUXT_SESSION_PASSWORD --name \u003Cworker>\n",[182,610,611],{"__ignoreMap":292},[296,612,613,615,617,619,622,625,628,631,633,636,639],{"class":298,"line":12},[296,614,302],{"class":301},[296,616,306],{"class":305},[296,618,309],{"class":305},[296,620,621],{"class":305}," secret",[296,623,624],{"class":305}," put",[296,626,627],{"class":305}," NUXT_SESSION_PASSWORD",[296,629,630],{"class":321}," --name",[296,632,434],{"class":433},[296,634,635],{"class":305},"worke",[296,637,638],{"class":440},"r",[296,640,444],{"class":433},[178,642,643,646,647,649,650,653,654,657,658,661,662,664],{},[182,644,645],{},"--name"," is required because ",[182,648,240],{}," has no ",[182,651,652],{},"name",". Nitro derives one\nfrom the ",[182,655,656],{},"origin"," git remote, or the ",[182,659,660],{},"package.json"," name without one, and\n",[182,663,250],{}," prints it as \"Using auto generated worker name\".",[509,666,667,677],{},[512,668,669],{},[515,670,671,674],{},[518,672,673],{},"Variable",[518,675,676],{},"Needed when",[528,678,679,689,699,713,727,746,763,778,793],{},[515,680,681,686],{},[533,682,683],{},[182,684,685],{},"NUXT_SESSION_PASSWORD",[533,687,688],{},"Always. 32 characters or more",[515,690,691,696],{},[533,692,693],{},[182,694,695],{},"NUXT_OG_IMAGE_SECRET",[533,697,698],{},"Always. Any random string. Unset, each build signs OG image URLs with its own, and older URLs answer 403",[515,700,701,706],{},[533,702,703],{},[182,704,705],{},"NUXT_TURNSTILE_SECRET_KEY",[533,707,708,709,712],{},"Auth or the contact page is on. Forms skip the check until both Turnstile keys are set, and ",[182,710,711],{},"\u002Fcontact"," answers 503",[515,714,715,724],{},[533,716,717,720,721],{},[182,718,719],{},"NUXT_EMAIL_FROM",", ",[182,722,723],{},"NUXT_EMAIL_FROM_NAME",[533,725,726],{},"Email is on. Without a sender every mail is refused with a 501, so nobody can verify an address",[515,728,729,740],{},[533,730,731,720,734,720,737],{},[182,732,733],{},"NUXT_PAYMENTS_STRIPE_SECRET_KEY",[182,735,736],{},"NUXT_PAYMENTS_STRIPE_WEBHOOK_SECRET",[182,738,739],{},"NUXT_PAYMENTS_STRIPE_REFS_*",[533,741,742,743,745],{},"Payments are on, as shipped. Checkout answers 500 until they are set (",[198,744,59],{"href":62},")",[515,747,748,756],{},[533,749,750,720,753],{},[182,751,752],{},"NUXT_OAUTH_\u003CPROVIDER>_CLIENT_ID",[182,754,755],{},"NUXT_OAUTH_\u003CPROVIDER>_CLIENT_SECRET",[533,757,758,759,762],{},"One pair per provider in ",[182,760,761],{},"auth.oauth",", which ships empty",[515,764,765,770],{},[533,766,767],{},[182,768,769],{},"NUXT_MONITORING_\u003CPROVIDER>_DSN",[533,771,772,775,776,745],{},[182,773,774],{},"monitoring.enabled"," is on (",[198,777,135],{"href":134},[515,779,780,785],{},[533,781,782],{},[182,783,784],{},"NUXT_CONTACT_ADDRESS",[533,786,787,788,790,791,745],{},"The contact page is on, as shipped. ",[182,789,711],{}," answers 503 until it is set (",[198,792,91],{"href":90},[515,794,795,800],{},[533,796,797],{},[182,798,799],{},"NUXT_BLOG_AUTHOR_EMAIL",[533,801,802],{},"Optional. Read per request, so changing it needs no rebuild",[178,804,805,812,813,816,817,820,821,824],{},[198,806,809],{"href":807,"rel":808,"title":203},"https:\u002F\u002Fgithub.com\u002Fnuxter-app\u002Fkit\u002Fblob\u002Fmain\u002F.env.example",[202],[182,810,811],{},".env.example"," lists every variable the kit reads and where\neach value comes from. After a cold start the Worker logs one ",[182,814,815],{},"[env]"," line\nnaming everything ",[182,818,819],{},"starter.config.ts"," turns on that this deploy has no\ncredentials for. Read it in ",[182,822,823],{},"wrangler tail"," after the first deploy.",[191,826,828],{"id":827},"add-your-domain","Add your domain",[178,830,831,832,835,836,839,840,842],{},"The kit ships no ",[182,833,834],{},"routes",", so the Worker answers on its ",[182,837,838],{},"*.workers.dev"," address.\nAdd your domain at the top level of ",[182,841,240],{},":",[287,844,848],{"className":845,"code":846,"language":847,"meta":292,"style":292},"language-jsonc shiki shiki-themes github-light github-light github-dark","\"routes\": [{ \"pattern\": \"example.com\", \"custom_domain\": true }],\n","jsonc",[182,849,850],{"__ignoreMap":292},[296,851,852],{"class":298,"line":12},[296,853,846],{},[178,855,856,858,859,862],{},[182,857,268],{}," creates the DNS record and the certificate, but only for a\nzone in the same Cloudflare account. Anywhere else the deploy fails. ",[182,860,861],{},"www"," needs\na second entry or a redirect rule, or it won't resolve.",[178,864,865],{},"Before the first deploy on that zone, turn off two settings Cloudflare enables\nby default:",[867,868,869,876],"ul",{},[222,870,871,872,875],{},"Speed Brain (Speed > Settings > Content Optimization). Cloudflare answers its\nprefetches only from cache and never from a Worker, so every prefetched page\nand ",[182,873,874],{},"\u002F_nuxt\u002F"," chunk gets a 503.",[222,877,878,879,470],{},"Web Analytics automatic setup (Web Analytics > your site > Manage site). It\ninjects a beacon script that the kit's Content-Security-Policy blocks, so\nevery page logs a CSP violation. The kit's own analytics run on the server\n(",[198,880,130],{"href":129},[178,882,883,884,886],{},"Point ",[182,885,573],{}," at the domain and deploy again.",[191,888,890],{"id":889},"deploy-from-your-machine","Deploy from your machine",[287,892,894],{"className":289,"code":893,"language":291,"meta":292,"style":292},"cp .env.production.example .env.production\npnpm deploy:cf\n",[182,895,896,907],{"__ignoreMap":292},[296,897,898,901,904],{"class":298,"line":12},[296,899,900],{"class":301},"cp",[296,902,903],{"class":305}," .env.production.example",[296,905,906],{"class":305}," .env.production\n",[296,908,909,911],{"class":298,"line":17},[296,910,302],{"class":301},[296,912,913],{"class":305}," deploy:cf\n",[178,915,916,917,920,921,720,923,925,926,928,929,931,932,935,936,939,940,942,943,455,946,949],{},"It runs the same steps as the workflow: preflight, build, migrate, deploy.\n",[182,918,919],{},".env.production"," holds ",[182,922,244],{},[182,924,573],{}," and\n",[182,927,589],{},", and the build reads it instead of ",[182,930,492],{},", so\nthe ",[182,933,934],{},"http:\u002F\u002Flocalhost:3000"," there never ends up in a mailed link. Wrangler\ndeploys with your ",[182,937,938],{},"wrangler login",", or with ",[182,941,537],{}," when it is\nset. ",[182,944,945],{},"pnpm db:migrate:remote",[182,947,948],{},"pnpm db:studio:remote"," read the same file.",[191,951,953],{"id":952},"ship-a-migration-safely","Ship a migration safely",[178,955,956],{},"Migrations run before the new Worker is uploaded, so for a short time the old\ncode runs against the new schema. Adding tables, columns and indexes is safe.\nDropping or narrowing something the live code still uses, such as a column it\nreads or the unique index an upsert names as its conflict target, breaks the old\nWorker until the new one lands, and for good if the deploy fails.",[178,958,959],{},"Split such a change across two releases: add the new shape and deploy, then drop\nthe old one in the next pull request.",[178,961,962,963,965],{},"There is no rollback step. Revert the commit on ",[182,964,184],{}," and let it deploy.",[191,967,240],{"id":968},"wranglerjsonc",[178,970,971,977,978,980],{},[198,972,975],{"href":973,"rel":974,"title":203},"https:\u002F\u002Fgithub.com\u002Fnuxter-app\u002Fkit\u002Fblob\u002Fmain\u002Fwrangler.jsonc",[202],[182,976,240],{}," declares every binding the Worker gets.\nRun ",[182,979,462],{}," after you add or rename one.",[509,982,983,996],{},[512,984,985],{},[515,986,987,990,993],{},[518,988,989],{},"Key",[518,991,992],{},"Binding",[518,994,995],{},"What it is for",[528,997,998,1020,1034,1055,1074,1091,1115,1149,1166,1185,1202],{},[515,999,1000,1004,1009],{},[533,1001,1002],{},[182,1003,450],{},[533,1005,1006],{},[182,1007,1008],{},"HYPERDRIVE",[533,1010,1011,1012,1015,1016,1019],{},"Postgres. ",[182,1013,1014],{},"localConnectionString"," is the database ",[182,1017,1018],{},"pnpm preview"," uses",[515,1021,1022,1026,1031],{},[533,1023,1024],{},[182,1025,454],{},[533,1027,1028],{},[182,1029,1030],{},"DB",[533,1032,1033],{},"Nuxt Content's pages. No app table lives there",[515,1035,1036,1040,1045],{},[533,1037,1038],{},[182,1039,458],{},[533,1041,1042],{},[182,1043,1044],{},"CACHE",[533,1046,1047,1048,1051,1052],{},"Nitro's cache, behind the ",[182,1049,1050],{},"swr"," route rules such as ",[182,1053,1054],{},"\u002Fblog",[515,1056,1057,1062,1067],{},[533,1058,1059],{},[182,1060,1061],{},"r2_buckets",[533,1063,1064],{},[182,1065,1066],{},"AVATARS",[533,1068,1069,1070,1073],{},"Uploaded avatars. The build checks that every ",[182,1071,1072],{},"storage.purposes"," entry names a bucket declared here",[515,1075,1076,1081,1086],{},[533,1077,1078],{},[182,1079,1080],{},"queues",[533,1082,1083],{},[182,1084,1085],{},"QUEUE",[533,1087,1088,1089,745],{},"Background jobs and their dead letter queue (",[198,1090,76],{"href":75},[515,1092,1093,1101,1106],{},[533,1094,1095,720,1098],{},[182,1096,1097],{},"durable_objects",[182,1099,1100],{},"migrations",[533,1102,1103],{},[182,1104,1105],{},"AUTH_THROTTLE",[533,1107,1108,1109,1112,1113,745],{},"The counters behind ",[182,1110,1111],{},"requireRateLimit"," and sign-in lockout (",[198,1114,125],{"href":124},[515,1116,1117,1122,1130],{},[533,1118,1119],{},[182,1120,1121],{},"ratelimits",[533,1123,1124,720,1127],{},[182,1125,1126],{},"AUTH_RATE_LIMIT",[182,1128,1129],{},"ANALYTICS_RATE_LIMIT",[533,1131,1132,1133,1136,1137,1140,1141,1144,1145,1148],{},"Coarse limits in front of those counters and the analytics ingest. ",[182,1134,1135],{},"period"," takes only ",[182,1138,1139],{},"10"," or ",[182,1142,1143],{},"60",", and ",[182,1146,1147],{},"namespace_id"," is any number you pick",[515,1150,1151,1156,1161],{},[533,1152,1153],{},[182,1154,1155],{},"analytics_engine_datasets",[533,1157,1158],{},[182,1159,1160],{},"ANALYTICS",[533,1162,473,1163,1165],{},[182,1164,466],{}," analytics sink. The dataset is created on its first write",[515,1167,1168,1173,1177],{},[533,1169,1170],{},[182,1171,1172],{},"ai",[533,1174,1175],{},[182,1176,111],{},[533,1178,1179,1180,1182,1183,745],{},"Workers AI. Always remote, so calls from ",[182,1181,496],{}," are billed too (",[198,1184,111],{"href":110},[515,1186,1187,1192,1197],{},[533,1188,1189],{},[182,1190,1191],{},"send_email",[533,1193,1194],{},[182,1195,1196],{},"EMAIL",[533,1198,473,1199,1201],{},[182,1200,466],{}," mail provider",[515,1203,1204,1209,1212],{},[533,1205,1206],{},[182,1207,1208],{},"triggers.crons",[533,1210,1211],{},"none",[533,1213,1214],{},"When scheduled tasks run. The build fails without one while auth or activity retention is on",[178,1216,1217,720,1219,455,1221,1224,1225,1228,1229,1232,1233,1236,1237,1240,1241,1244],{},[182,1218,652],{},[182,1220,184],{},[182,1222,1223],{},"assets"," are left out because nitro sets them.\n",[182,1226,1227],{},"compatibility_flags"," is empty, but the build adds ",[182,1230,1231],{},"nodejs_compat",", which the\nkit needs: the ",[182,1234,1235],{},"pg"," driver and runtime config both depend on it.\n",[182,1238,1239],{},"observability.enabled"," turns on Workers Logs, and ",[182,1242,1243],{},"keep_vars"," keeps plain-text\nvariables you set in the dashboard across deploys.",[191,1246,20],{"id":1247},"configuration",[509,1249,1250,1266],{},[512,1251,1252],{},[515,1253,1254,1257,1260,1263],{},[518,1255,1256],{},"Key or variable",[518,1258,1259],{},"Where",[518,1261,1262],{},"Default",[518,1264,1265],{},"What it does",[528,1267,1268,1284,1301,1317,1335,1358],{},[515,1269,1270,1274,1278,1281],{},[533,1271,1272],{},[182,1273,652],{},[533,1275,1276],{},[182,1277,240],{},[533,1279,1280],{},"derived by nitro",[533,1282,1283],{},"Pins the Worker's name",[515,1285,1286,1290,1294,1296],{},[533,1287,1288],{},[182,1289,834],{},[533,1291,1292],{},[182,1293,240],{},[533,1295,1211],{},[533,1297,1298,1299],{},"Custom domains. Without one the Worker is on ",[182,1300,838],{},[515,1302,1303,1307,1312,1314],{},[533,1304,1305],{},[182,1306,573],{},[533,1308,1309,1310],{},"GitHub variable, ",[182,1311,919],{},[533,1313,1211],{},[533,1315,1316],{},"The site's URL, read at build",[515,1318,1319,1324,1327,1329],{},[533,1320,1321],{},[182,1322,1323],{},"NUXT_PUBLIC_SITE_URL",[533,1325,1326],{},"Worker variable",[533,1328,1211],{},[533,1330,1331,1332,1334],{},"Overrides ",[182,1333,573],{}," without a rebuild",[515,1336,1337,1342,1345,1349],{},[533,1338,1339],{},[182,1340,1341],{},"NUXT_IMAGE_PROVIDER",[533,1343,1344],{},"build environment",[533,1346,1347],{},[182,1348,1211],{},[533,1350,1351,1352,1355,1356],{},"Turns on image optimization with a ",[182,1353,1354],{},"@nuxt\u002Fimage"," provider, such as ",[182,1357,466],{},[515,1359,1360,1364,1369,1371],{},[533,1361,1362],{},[182,1363,244],{},[533,1365,1366,1367],{},"GitHub secret, ",[182,1368,919],{},[533,1370,1211],{},[533,1372,1373],{},"Where the deploy migrates. The Worker itself never reads it",[191,1375,1377],{"id":1376},"good-to-know","Good to know",[867,1379,1380,1394,1407,1410,1416],{},[222,1381,1382,1383,1385,1386,1389,1390,1393],{},"Two shipped defaults need the Workers Paid plan. On Free, the ",[182,1384,466],{},"\nmail provider delivers only to addresses verified in your account, so list\n",[182,1387,1388],{},"resend"," in ",[182,1391,1392],{},"email.providers"," instead. Password hashing takes about 17 ms of\nCPU against Free's 10 ms limit, so steady password sign-ins end in Error 1102.",[222,1395,1396,1397,1400,1401,1404,1405,217],{},"Images ship unoptimized: a 4 MB file in ",[182,1398,1399],{},"public\u002F"," reaches every visitor at\n4 MB. ",[182,1402,1403],{},"NUXT_IMAGE_PROVIDER=cloudflare"," at build time needs Image\nTransformations on the zone and does nothing on ",[182,1406,838],{},[222,1408,1409],{},"The first deploy starts with an empty database. The first account to sign up\nand verify its address becomes superadmin, so register yours right away.",[222,1411,1412,1413,217],{},"The preflight's caching check needs Account > Hyperdrive > Read on the token.\nWithout it the run logs a warning and deploys anyway, so check the setting\nonce with ",[182,1414,1415],{},"pnpm exec wrangler hyperdrive get \u003Cid>",[222,1417,1418,1420,1421,1423,1424,1427,1428,470],{},[182,1419,496],{}," runs on Node, not on workerd. Try Worker-only behaviour with\n",[182,1422,1018],{},", which builds the Worker and runs it under ",[182,1425,1426],{},"wrangler dev","\nagainst a real Postgres (",[198,1429,71],{"href":70},[1431,1432,1433],"style",{},"html pre.shiki code .snPdu, html code.shiki .snPdu{--shiki-light:#6F42C1;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sIIMD, html code.shiki .sIIMD{--shiki-light:#032F62;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sBjJW, html code.shiki .sBjJW{--shiki-light:#005CC5;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .s8jYJ, html code.shiki .s8jYJ{--shiki-light:#D73A49;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sxrX7, html code.shiki .sxrX7{--shiki-light:#24292E;--shiki-default:#24292E;--shiki-dark:#E1E4E8}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);}",{"title":292,"searchDepth":17,"depth":17,"links":1435},[1436,1437,1438,1439,1440,1441,1442,1443,1444],{"id":193,"depth":17,"text":194},{"id":275,"depth":17,"text":276},{"id":503,"depth":17,"text":504},{"id":827,"depth":17,"text":828},{"id":889,"depth":17,"text":890},{"id":952,"depth":17,"text":953},{"id":968,"depth":17,"text":240},{"id":1247,"depth":17,"text":20},{"id":1376,"depth":17,"text":1377},[1446,1447],{"path":24,"title":25,"description":26,"navTitle":25,"group":6,"order":27},{"path":34,"title":35,"description":36,"navTitle":35,"group":6,"order":37},true]