[{"data":1,"prerenderedAt":318},["ShallowReactive",2],{"blog-nuxt-saas-boilerplate-for-ai-agents":3,"related-nuxt-saas-boilerplate-for-ai-agents":68},{"id":4,"title":5,"author":6,"body":7,"category":6,"date":53,"description":54,"draft":55,"extension":56,"image":6,"meta":57,"navigation":59,"path":60,"seo":61,"stem":62,"tags":63,"__hash__":67},"blog\u002Fblog\u002Fnuxt-saas-boilerplate-for-ai-agents.md","Every boilerplate ships the stack. That was never the hard part.",null,{"type":8,"value":9,"toc":49},"minimark",[10,14,17,20,23,26,29,32,35,43,46],[11,12,13],"p",{},"Every SaaS starter I ever bought handed me the same box of parts. Auth, a database client, a Stripe integration, a pile of Tailwind, a landing page with a hero section. Then it left me alone in a room with the box and a README.",[11,15,16],{},"The parts were never the problem. Bolting them to each other was the part that ate my weekends.",[11,18,19],{},"You know the work I mean. The Stripe webhook that has to agree with your database. The RLS policy you forgot about until a user saw someone else's data. The five places a new locale string has to land before the build stops complaining. None of it is hard in the sense of being clever. It is hard in the sense of being endless, slightly different in every project, and exactly the kind of thing you get wrong at 11pm.",[11,21,22],{},"I built one SaaS, then a second, and by the third I noticed I was re-typing the same 80 percent before I got anywhere near the part that made the product mine. So I did what every developer does. I started a boilerplate.",[11,24,25],{},"Then coding agents got good, and the whole shape of the problem changed.",[11,27,28],{},"Here is the thing nobody told me about building with an agent. Generating the first version of anything is now fast. Embarrassingly fast. Point Claude Code at an empty Nuxt project and ask for a login page, and you have one before your coffee is poured. The slow part moved. It is now the second month, when the agent has written forty files and half of them fetch data one way and half another, the auth check exists in three different shapes, and you are the one holding all of it in your head because nothing wrote the rules down.",[11,30,31],{},"A boilerplate that only helps the human is solving last year's problem.",[11,33,34],{},"So BoiledPlate is built the other way around. The conventions are written for the agent first. There is one documented way to read data, one way to gate a route, one way to add a locale string, one way to take money. Not because I think you are incapable of inventing a second way, but because the agent will invent a fifth and a sixth unless something tells it not to. The rules live in a file the agent reads on its own, next to skills it consults before it touches Supabase or Stripe, next to a read-only view of your actual schema so it stops guessing at column names.",[11,36,37,38,42],{},"The first session does not start by generating code. It starts by asking you nine questions. What is the product called, which languages, which billing model, do you want the blog, that sort of thing. Then it reshapes the repo to your answers using patches written ahead of time, so the result is the same every time instead of whatever the model felt like that morning. Secret keys go straight into your ",[39,40,41],"code",{},".env"," and never pass through the chat.",[11,44,45],{},"I am not going to pretend this removes all the work. You still have to build the thing only you can build. But the box of parts is wired together when you open it, and the thing assembling your product knows the house rules.",[11,47,48],{},"I sold it once and I keep building on it. boiledplate.ai runs on BoiledPlate, set up by the same nine questions you would get. That is the only honesty test I trust for a starter. If I would not ship my own product on it, why would I ask you to.",{"title":50,"searchDepth":51,"depth":51,"links":52},"",3,[],"2026-05-12","Starters hand you a box of parts and a README. The wiring is what ate my weekends, and with coding agents the slow part moved somewhere else entirely.",false,"md",{"slug":58},"nuxt-saas-boilerplate-for-ai-agents",true,"\u002Fblog\u002Fnuxt-saas-boilerplate-for-ai-agents",{"title":5,"description":54},"blog\u002Fnuxt-saas-boilerplate-for-ai-agents",[64,65,66],"saas","boilerplate","ai-agents","GlGOq0IBTpwQVdHc_qua8-PwCLV38qRl5i8dfd-R-BI",[69,161,265],{"id":70,"title":71,"author":6,"body":72,"category":6,"date":148,"description":149,"draft":55,"extension":56,"image":6,"meta":150,"navigation":59,"path":152,"seo":153,"stem":154,"tags":155,"__hash__":160},"blog\u002Fblog\u002Fnuxt-supabase-stripe-saas-boilerplate.md","What it actually takes to wire up a Nuxt, Supabase and Stripe SaaS",{"type":8,"value":73,"toc":140},[74,77,80,83,88,91,94,98,101,110,114,117,120,124,127,131,134,137],[11,75,76],{},"If you are about to build a SaaS, the stack writes itself these days. Nuxt for the app, Supabase for the database and auth, Stripe for payments, Resend for email. It is a good stack. I have shipped on it more than once and I would pick it again tomorrow.",[11,78,79],{},"Then you open an empty project and remember that picking the stack was the easy decision. Wiring four services into something that does not leak data or lose payments is the part that takes the weeks.",[11,81,82],{},"Here is what each piece actually asks of you, roughly in the order it tends to bite.",[84,85,87],"h2",{"id":86},"supabase-auth-is-the-fast-part-rls-is-the-part-you-forget","Supabase: auth is the fast part, RLS is the part you forget",[11,89,90],{},"Getting a login form working with Supabase takes an afternoon. Email and password, maybe Google OAuth, a session, a redirect. Feels done.",[11,92,93],{},"It is not done. The moment you have more than one user, every table needs row-level security, and you need it from the first migration, not bolted on after launch when you are already holding real data. RLS is not optional polish. It is the only thing standing between user A and user B's rows. Get a policy wrong and the bug does not throw an error, it quietly serves the wrong data to the wrong person. You also want password recovery that does not reveal whether an email exists, route protection that lives in middleware instead of scattered redirects, and generated types so you stop guessing at column names.",[84,95,97],{"id":96},"stripe-the-webhook-is-the-product-the-checkout-is-the-easy-bit","Stripe: the webhook is the product, the checkout is the easy bit",[11,99,100],{},"Everyone wires the checkout first because it is satisfying. Money goes in, you get redirected, you feel like a founder.",[11,102,103,104,109],{},"The redirect is the least trustworthy thing in your whole system. The real billing state has to come from Stripe's webhooks, signature-verified and idempotent, because the browser will close the tab and the network will deliver the same event twice. Then there is the part most starters skip: a refund has to revoke access on its own, and plan limits have to be enforced in the database, not hidden in the UI. None of this is hard once. It is a lot of small correct decisions, and any one of them wrong is a billing bug in production. I wrote about ",[105,106,108],"a",{"href":107},"\u002Fblog\u002Fstripe-webhooks-source-of-truth","the webhook trap in more detail here"," if you want the longer version.",[84,111,113],{"id":112},"resend-simple-to-send-annoying-to-send-exactly-once","Resend: simple to send, annoying to send exactly once",[11,115,116],{},"Sending one email with Resend is three lines. The trouble starts when a Stripe retry fires a second payment-confirmation email, or a flaky request makes you send the welcome email twice. You want a single server-side utility, sends that are idempotent for anything a user can trigger, and every line of those emails living as a locale key so a German customer does not get an English receipt.",[11,118,119],{},"Which brings up the thing that quietly doubles the work on everything above.",[84,121,123],{"id":122},"nuxt-is-the-glue-and-i18n-is-the-tax-on-doing-it-right","Nuxt is the glue, and i18n is the tax on doing it right",[11,125,126],{},"Every string in all of this, the auth labels, the validation messages, the toasts, the Stripe error states, the email bodies, has to exist in every language you support. Tack i18n on at the end and you will spend a week hunting hardcoded strings. Build it in from the first component and it is just a habit. Shipping four languages on day one is a very different project from shipping one and promising the rest.",[84,128,130],{"id":129},"so-how-long-is-all-of-that","So how long is all of that",[11,132,133],{},"For me, the first time, weeks. Not because any single piece is clever, but because the correct version of each one hides about five non-obvious decisions, and you only learn which five by getting them wrong.",[11,135,136],{},"That is the entire reason BoiledPlate exists. It is a Nuxt 4 SaaS starter on exactly this stack, Supabase, Stripe and Resend, already wired the right way. RLS on every table from the first migration. The Stripe webhook as the source of truth, refund-aware, with limits in the policies. One idempotent email utility, localized. Four languages shipped, not promised. You point a coding agent at it, answer nine questions, and the thing reshapes itself to your product instead of you reshaping yourself to it.",[11,138,139],{},"You can absolutely wire this yourself. The stack is good and the docs are all there. I just think the second time you do it, you start wondering why you are paying the same tax twice.",{"title":50,"searchDepth":51,"depth":51,"links":141},[142,144,145,146,147],{"id":86,"depth":143,"text":87},2,{"id":96,"depth":143,"text":97},{"id":112,"depth":143,"text":113},{"id":122,"depth":143,"text":123},{"id":129,"depth":143,"text":130},"2026-06-09","Picking a Nuxt + Supabase + Stripe + Resend stack is the easy decision. Here is the wiring each piece really demands, and where a SaaS starter saves you weeks.",{"slug":151},"nuxt-supabase-stripe-saas-boilerplate","\u002Fblog\u002Fnuxt-supabase-stripe-saas-boilerplate",{"title":71,"description":149},"blog\u002Fnuxt-supabase-stripe-saas-boilerplate",[156,157,158,159,64,65],"nuxt","supabase","stripe","resend","O9aBZHsolIkxENJU_ebdFE9p0IW2dowSQQ-IXutuW_M",{"id":162,"title":163,"author":6,"body":164,"category":6,"date":148,"description":256,"draft":55,"extension":56,"image":6,"meta":257,"navigation":59,"path":259,"seo":260,"stem":261,"tags":262,"__hash__":264},"blog\u002Fblog\u002Fstripe-subscriptions-nuxt-supabase.md","How to set up Stripe subscriptions in a Nuxt + Supabase app",{"type":8,"value":165,"toc":248},[166,169,172,176,197,201,207,211,218,222,231,235,238,242,245],[11,167,168],{},"Subscriptions are the part of a SaaS where the demo and the reality diverge most. The happy path, where a user clicks subscribe, pays, and gets access, is maybe a fifth of the work. The other four fifths is everything that happens after, and Stripe plus Supabase have a clean division of labor for it if you set things up in the right order.",[11,170,171],{},"Here is the order that has worked for me on a Nuxt + Supabase app.",[84,173,175],{"id":174},"_1-define-your-plans-in-one-place","1. Define your plans in one place",[11,177,178,179,182,183,182,186,189,190,192,193,196],{},"Before any Stripe code, decide what your plans are and give each a slug your app understands, like ",[39,180,181],{},"free",", ",[39,184,185],{},"pro",[39,187,188],{},"team",". Keep the slugs in your codebase and keep the Stripe price IDs in an environment map next to them. The reason to separate the two: your app should reason about ",[39,191,185],{},", not about ",[39,194,195],{},"price_1Q2x...",", and you will have different price IDs in test and live mode anyway. One source of truth for the slugs, one env map for the IDs.",[84,198,200],{"id":199},"_2-checkout-is-a-redirect-nothing-more","2. Checkout is a redirect, nothing more",[11,202,203,204],{},"Create a Stripe Checkout session on the server, hand the URL to the browser, redirect. Do not record anything about the subscription at this point. The checkout session succeeding tells you the user started paying, not that they are entitled to anything yet. Resist the urge to flip a flag on the success page. ",[105,205,206],{"href":107},"That instinct is the single most common billing bug, and here is why.",[84,208,210],{"id":209},"_3-the-webhook-is-where-subscriptions-actually-live","3. The webhook is where subscriptions actually live",[11,212,213,214,217],{},"This is the heart of it. Stripe sends events for the whole lifecycle: subscription created, renewed, payment failed, cancelled, refunded. Your webhook handler, signature-verified and idempotent, is the only thing that writes subscription state to Supabase. When ",[39,215,216],{},"customer.subscription.updated"," says the plan changed, you update the row. When it says the payment is past due, you reflect that. The database is a mirror of what Stripe believes, never a guess your frontend made.",[84,219,221],{"id":220},"_4-turn-subscription-state-into-access","4. Turn subscription state into access",[11,223,224,225,227,228,230],{},"Now you have a row in Supabase that says this user is on ",[39,226,185],{}," and the subscription is active. Access control reads from that row. The important part, again, is where you enforce it: in your row-level security policies, so a ",[39,229,185],{},"-only table is genuinely unreachable for a free user, not just visually hidden. The UI can hide buttons for nice UX, but the policy is what makes the limit real.",[84,232,234],{"id":233},"_5-give-them-the-customer-portal","5. Give them the customer portal",[11,236,237],{},"You do not want to build cancellation, card updates, and invoice history yourself. Stripe's customer portal does all of it. Generate a portal session on the server, redirect the user, and let your webhook pick up whatever they change there. One less set of screens to build and keep correct.",[84,239,241],{"id":240},"the-shape-of-it","The shape of it",[11,243,244],{},"Plans in config. Checkout as a dumb redirect. Webhook as the only writer of state. RLS as the enforcer. Portal for self-service. Get those five right and subscriptions stop being scary, because every piece has exactly one job.",[11,246,247],{},"If you would rather not assemble all five by hand, this is the billing setup BoiledPlate ships by default on a Nuxt 4 + Supabase stack. Subscriptions, one-time, or none, chosen when the starter sets itself up, with the webhook, the RLS limits and the portal already wired. Think of it as the version of this post where someone already made the five decisions, so you just build your product on top.",{"title":50,"searchDepth":51,"depth":51,"links":249},[250,251,252,253,254,255],{"id":174,"depth":143,"text":175},{"id":199,"depth":143,"text":200},{"id":209,"depth":143,"text":210},{"id":220,"depth":143,"text":221},{"id":233,"depth":143,"text":234},{"id":240,"depth":143,"text":241},"The five decisions behind Stripe subscriptions on a Nuxt + Supabase stack: plans in config, checkout, webhooks, RLS-enforced access, and the customer portal.",{"slug":258},"stripe-subscriptions-nuxt-supabase","\u002Fblog\u002Fstripe-subscriptions-nuxt-supabase",{"title":163,"description":256},"blog\u002Fstripe-subscriptions-nuxt-supabase",[158,263,156,157,64],"subscriptions","WgV2n4beSVMVXw3OM_Wp6Q6RGGHUo73mCDuPwU0S7FM",{"id":266,"title":267,"author":6,"body":268,"category":6,"date":306,"description":307,"draft":55,"extension":56,"image":6,"meta":308,"navigation":59,"path":310,"seo":311,"stem":312,"tags":313,"__hash__":317},"blog\u002Fblog\u002Fconventions-for-ai-coding-agents.md","Write your conventions for the agent, not the next hire",{"type":8,"value":269,"toc":304},[270,273,276,279,282,285,292,295,298,301],[11,271,272],{},"We have all worked in a codebase with three ways to do the same thing. One the founder wrote in a hurry, one a contractor left behind, one that is just whatever the last person copied off the internet. You learn to read all three. It is annoying but survivable, because a human can hold a contradiction in their head and pick the right pattern by smell.",[11,274,275],{},"A coding agent cannot smell anything.",[11,277,278],{},"Give an agent a codebase with three ways to fetch data and it will cheerfully add a fourth. Not because it is dumb, but because it has no reason to prefer yours. It pattern-matches against everything it has ever seen, and everything it has ever seen includes every bad tutorial ever published. The drift is not a one-time mess you clean up later. It compounds. Every file the agent writes becomes context for the next file, so one sloppy week teaches the agent to keep being sloppy.",[11,280,281],{},"The usual answer to this is a long prompt. You paste your preferences at the start of every session and hope they survive. They do not. The model loses them, the session resets, you forget to paste them, and you are back to three patterns and counting.",[11,283,284],{},"The better answer is to write the rules down where the agent will find them on its own.",[11,286,287,288,291],{},"That is what ",[39,289,290],{},"AGENTS.md"," is. Not documentation for the next human hire, though it works for that too. It is a contract the agent reads before it works. One documented way to access data. One way to handle secrets. One way to gate an authenticated route. One way to add a migration, a locale string, a Stripe price. The agent does not have to guess your taste, because your taste is written down in a place it actually looks.",[11,293,294],{},"Writing it down is half the job. The other half is handing the agent the current facts instead of a stale memory of them. An agent that guesses your database columns will guess wrong, and confidently. So BoiledPlate ships read-only MCP servers that let the agent look at your real Supabase schema, the actual Nuxt UI component docs, and your Stripe test data while it builds. It is the difference between an assistant who read the manual once and one who has the manual open on the desk.",[11,296,297],{},"There is one more layer, and it is the one I am most attached to. Vendor skills. The Supabase team, the Stripe team, and the Nuxt UI team all have opinions about how their tools should be used, and those opinions live in skills the agent can consult. I vendor them into the repo, hash-locked, refreshed by a weekly CI pull request so they do not silently rot. On top of those I wrote one of my own for Nuxt 4 patterns, with every rule anchored to a real file in the repo so the agent can go read the example instead of trusting a description of it.",[11,299,300],{},"Put together, the agent building in BoiledPlate is not improvising your conventions from memory. It is reading them, checking them against your live schema, and following skills written by the people who made the tools. The output stays consistent in month three because consistency was designed in, not nagged in.",[11,302,303],{},"The old instinct says write clean code and document it for whoever comes next. That instinct is right. It is just aimed at the wrong reader now. The next contributor to your codebase is an agent, and it will follow your rules exactly as well as you wrote them down.",{"title":50,"searchDepth":51,"depth":51,"links":305},[],"2026-06-08","An agent cannot smell a codebase. Give it three ways to do one thing and it adds a fourth. Here is how BoiledPlate keeps agent output consistent past month three.",{"slug":309},"conventions-for-ai-coding-agents","\u002Fblog\u002Fconventions-for-ai-coding-agents",{"title":267,"description":307},"blog\u002Fconventions-for-ai-coding-agents",[66,314,315,316],"claude-code","conventions","developer-experience","NsQDMtlYDClnjq1hfj5qm_qUO25Y8pNl1Gi3j6Qd3rc",1780964587329]