Skip to content
Vibe Coders

From v0 to a Working App: The Backend You Still Have to Build

v0 draws the frontend. You still need auth, database, API routes, deployment, and payments. A reality check for founders who think one v0 prompt ships a product.

By WitsCode10 min read

You typed a prompt into v0 and got back a dashboard that looks better than most YC demo days. Sidebar, data table, auth pages, empty states, a pricing toggle. You clicked deploy, got a production URL, and sent it to three friends. The first said it looks great. The second tried to sign up and nothing happened. The third signed up, paid, then refreshed the page and was logged out. You went back to v0, prompted for a fix, and now the dashboard has a new chart but signup is still broken.

The problem is not v0. v0 is doing exactly what it advertises: generating interface. The problem is that in a working SaaS, interface is the minority of the code. What v0 shows you is roughly half the product, and it is the visible, satisfying, portfolio-ready half. The other half is auth that does not lose sessions, a database with rules about who can read what, webhook endpoints that verify signatures, payment flows that survive a declined card, emails that do not go to spam, jobs that run when nobody is looking, and logging that tells you why something broke at 2am. None of that is in the preview URL.

This piece walks through what v0 actually ships, what you still have to build, and the specific decisions that separate a v0 demo from a product a stranger can pay you for.

What v0 actually builds

v0 started life as a component generator. Prompt in, React plus Tailwind plus shadcn components out. Over the last year it grew into a fuller Next.js generator. It now scaffolds App Router projects, can wire up Supabase or Clerk, drops in Stripe checkout snippets, and gives you a one-click deploy to Vercel. If you look at a v0 project in the editor today, it is not a toy. It is real Next.js code.

What it is excellent at is the UI surface. Marketing pages, dashboards, data tables with sort and filter, form layouts, onboarding flows, auth page aesthetics, empty states, settings screens. It gets responsive behavior mostly right, uses decent accessibility primitives, and produces code a senior engineer can read without wincing.

What it does not do reliably is any of the thinking that has to happen before the UI makes sense. It does not design your data model. It does not write row-level security policies that match how your users actually group. It does not verify a Stripe webhook signature in a way that survives the middleware chain your app accumulates. It does not set up the environment separation between preview and production that keeps test Stripe charges from landing on real customers. It does not wire email so that verification links arrive in inboxes rather than spam folders. It does not pick between server actions and route handlers based on who is calling the endpoint. It scaffolds the shape of those things and leaves you holes to fill.

If you stop at "v0 wrote me a working app," you have a frontend with backend-shaped placeholders. The moment a second user hits it, you find out which placeholders were actually wired and which were decorative.

The fifty percent done problem

Think of a shipping SaaS as three layers stacked. The top is what users see. The middle is business logic: what happens when a user submits a form, what changes in the database, what emails fire, what gets charged. The bottom is infrastructure: deployment, environment variables, logging, backups, secrets, jobs, monitoring.

v0 covers the top layer at roughly 80 percent. The code it generates for a dashboard or a signup form is close to production quality. You will clean up copy, tighten up spacing, add your branding, swap an icon. You are not rewriting components.

v0 covers the middle layer at maybe 30 percent. It will import Supabase and call a function. It will import Stripe and create a checkout session. It will set up a Clerk provider at the root of your app. What it will not do is think about what happens when the checkout session succeeds but your webhook handler has not yet written the subscription record to the database and the user refreshes the page. It will not think about whether your RLS policies actually enforce that a user can only read their own rows. It will not design an idempotency strategy for a webhook that Stripe will retry seven times in thirty seconds.

v0 covers the bottom layer at roughly 5 percent. It picks Vercel for deploy, because v0 is a Vercel product, and that is the right default. Everything else in the infrastructure layer is on you. There is no error tracking, no structured logging, no uptime monitoring, no background jobs, no cron, no audit logs, no backups that you have tested, no runbook for when something goes wrong.

Founders look at the top layer, see something that looks finished, and conclude the whole product is finished. It is not. It is fifty percent done, and the fifty percent that remains is the fifty percent that determines whether customers stay.

The backend stack worth committing to

If you want to take a v0 project to something you can charge money for, you need to pick a backend stack and commit. The good news is that the defaults have converged. The bad news is that v0 will happily generate code for several of them without telling you which to stop shopping for.

For auth and database together, pick Supabase. It gives you Postgres, row-level security, auth (email, magic link, OAuth, phone OTP), storage, edge functions, and realtime in one project. The big advantage is that your auth identity and your row-level security policies share the same user ID, so you can write policies like "a user can only read their own orders" as a one-line Postgres rule. This is the path v0 pushes you toward most naturally, and for most early-stage SaaS it is the right call.

If you want auth as a dedicated product, pick Clerk. It is better at org structures, multi-session handling, user management, and MFA than most homegrown setups, and it is priced per monthly active user. Pair it with a database you pick separately: Neon for serverless Postgres with preview branches, or Turso for SQLite at the edge. With Clerk you own the auth experience; you also own wiring the Clerk user ID into every row of your database and handling the webhook that fires when a user is created.

For payments, pick Stripe unless you specifically need a merchant of record, in which case pick Paddle or LemonSqueezy. Stripe Checkout and the Customer Portal handle most of what you need without custom UI. The hard part of Stripe is not the initial integration; v0 handles that well enough. The hard part is the webhook that keeps your database in sync with Stripe's view of reality, the failed payment flow, the plan change flow, and tax calculation if you sell internationally.

For deployment, pick Vercel if you are on Next.js. The real work is not clicking deploy. The real work is environment variable discipline: a separate Supabase project for preview so your branches do not mutate production data, Clerk instances per environment, Stripe in test mode for non-production, and logging configured per environment.

For the rest, pick Sentry for errors, PostHog for product analytics, BetterStack or Axiom for logs and uptime, and Resend or Postmark for transactional email. None of these are optional past the first handful of users.

Server actions versus API routes, the decision v0 gets wrong

Next.js App Router gives you two ways to put backend code behind your frontend. Server actions are functions you mark with "use server" that the client can call directly, with React handling the serialization and revalidation. Route handlers are standard HTTP endpoints in app/api/*/route.ts that behave like any REST endpoint.

v0 has a strong preference for server actions. For your own forms, this is right. Server actions colocate the mutation with the component that triggers it, handle progressive enhancement so the form works without JavaScript, and integrate with revalidatePath and revalidateTag so the cache updates without manual refetching. A server action for "submit this onboarding form" is almost always the right call.

The problem is that v0 sometimes picks server actions for things that are not your own form. A Stripe webhook cannot call a server action, because Stripe speaks HTTP and expects an endpoint it can POST to with a specific content type and signature header. An OAuth callback from Google cannot call a server action. A mobile client cannot easily call a server action. A third-party integration cannot call a server action. If you ever plan to expose an endpoint to anything outside your own frontend, you need a route handler.

The rule is simple. If the only thing calling this code is your own UI, use a server action. If anything else calls it, including another service, a webhook, a mobile app, a curl command, or a future integration, use a route handler. The moment you try to wire a Stripe webhook into a v0-scaffolded project, you will find yourself converting a server action into a route handler, and this is the moment to get the pattern right for the rest of your webhooks.

While you are there, two things about webhook route handlers matter enough to call out. First, the body has to be read as raw text before signature verification, and any middleware that mutates the body (including body parsers or auth middleware) has to skip that path. Second, webhook handlers have to be idempotent, because Stripe will retry them, and "charge customer twice because the first retry succeeded after a timeout" is a support ticket you do not want.

The traps v0 will not warn you about

A few specific failures show up on almost every v0 project that ships to real users without a follow-up pass.

Row-level security is usually not enabled. Supabase tables default to RLS off in some workflows, and v0 is inconsistent. If RLS is off, every authenticated user can read every row. You can confirm this by logging in as user A and querying the table that should belong to user B. The fix is to enable RLS on every table and write policies that scope reads and writes to the authenticated user.

User sync between Clerk and your database is usually missing. Clerk is the source of truth for identity, but your app needs a users row to link orders and activity to. v0 sets up Clerk but rarely wires the user.created webhook that inserts the matching row. The symptom is users signing up successfully and then nothing working because the app cannot find them by ID.

Stripe webhooks are often set up but not verified correctly. The default v0 handler reads the body with the standard Next.js parser, which means signature verification fails silently, and the handler either runs on unverified events or never runs at all. The fix is to use the raw body, call stripe.webhooks.constructEvent with the signing secret, and return a 200 only after the database write has succeeded.

Email from your app often lands in spam. v0 wires up sending but will not configure DKIM, SPF, or DMARC, and defaults to a noreply address on a vercel.app subdomain. The fix is to use a real sending domain and set up the DNS records your email provider asks for.

File uploads often write to the wrong place. v0 sometimes stores uploaded files to the public folder or to /tmp. On Vercel, public is immutable after deploy and /tmp is scoped to a single invocation. The fix is to use Supabase Storage, S3, or Cloudflare R2 with signed URLs.

Preview deploys often hit production data. If you do not scope Supabase, Clerk, and Stripe to separate environments, every preview branch mutates your real database. The fix is separate projects per environment with Vercel branch-scoped variables.

What shipping actually means

A working SaaS is not a URL that loads. It is one where a stranger can find you, sign up without your help, pay you without your help, use the product without losing their data, cancel without emailing support, and come back a month later to a product that has not silently broken. Most of the work to make that true is invisible in a v0 preview.

This is why a v0 project that "feels done" often takes another two to four weeks to actually ship. Some of that work is careful engineering that nobody but you will see. Some of it is the boring operational setup that keeps the thing running when you are asleep. Some of it is the security review that means your first real customer does not become a lawsuit.

If you enjoy that work, do it yourself. The tools are good enough that a careful founder with a few months can get through it. If you do not, or if you have a window that closes before you can finish, hire someone who does.

When to bring in the finish

Your v0 output is a strong starting point, not a finished product. It is worth the ticket price of a subscription because it compresses the UI work from weeks to hours. The question is whether you use the time you saved to build the rest of the product or to prompt for another dashboard.

WitsCode picks up v0 projects at exactly this handoff. We take the scaffolded components, design the data model that fits your business, wire auth so sessions survive a refresh and RLS enforces the rules your users need, build webhook handlers with signature verification and idempotency, set up payments end to end including failed-payment and plan-change paths, configure email deliverability, stand up observability, separate environments so a branch cannot corrupt real data, and hand you back a product you can charge strangers for.

The work is usually two to four weeks from your v0 URL to a shipped product. The output is the same frontend you prompted for, with the half of the app you cannot see filled in properly. If you have a v0 project that feels ninety percent done and will not cross the finish line, that is the gap we close.

Get weekly field notes.

Practical writing on shipping products, straight to your inbox. No spam.

Need help with this?

MVP Development

We design and build web apps, MVPs, and SaaS products. Talk to us about what you are working on.

Talk to us

Want to discuss vibe coders for your business?

Start a project and we'll talk through where you are, what's working, and the highest-leverage moves for the next 90 days.