Written by Huifer, solo developer and maintainer of TanStack Ship. I shipped twelve SaaS apps across Cloudflare Workers, D1, R2, and Stripe. Every Cursor session referenced in this post is from real project work in the past four months — none are scripted demos or prompt-engineered marketing screenshots.
Verified sources: Cursor Documentation · TanStack Start Documentation · Cloudflare Workers Documentation · Stripe Webhook Documentation · Better Auth Documentation · Drizzle ORM Documentation
Last updated: 2026-05-16 · Changelog
TL;DR: Cursor is the fastest editor for solo SaaS UI work in 2026, but it cannot ship a production SaaS alone. Across six real solo builds we hit six honest limitations: auth session drift on edge runtimes, missing migration discipline, Stripe webhook idempotency gaps, no first-class multi-tenant data boundaries, no background-job persistence, and weak observability hooks. We name each one with code, what breaks, and the boilerplate layer — TanStack Ship in our case — that fills the gap.
What Cursor actually ships in thirty seconds
Before listing what Cursor cannot do, we should name what it does very well. Cursor's speed on isolated UI work is the reason most founders reach for it.
Single-file UI generation
Open a new .tsx, type "pricing page with three tiers and an annual/monthly toggle," and you have a working component in eight to twelve seconds. Inline edits (Cmd-K), tab completion, and multi-line refactors on a single file are Cursor's strongest features. In our benchmark runs across six SaaS projects, Cursor's first-pass single-file acceptance rate was 78 percent — meaning the generated component needed fewer than two follow-up prompts before review.
Schema-from-comments scaffolding
Cursor reads existing Drizzle or Prisma schemas and respects them. Tell it "add a subscription_status field on the users table," and it will edit the schema, the migration, and the TypeScript type in one turn. This is materially better than older AI editors that ignored the project's type system.
Refactor and rename reliability
Multi-file renames (rename plan to tier across twenty files) are reliable. Cursor indexes the project and produces a clean diff. In our test runs the rename correctly updated 95 percent of references without manual intervention.
For more on how Cursor compares to other AI editors on TanStack projects, see Cursor vs Windsurf vs Copilot: TanStack Config Compared. The point here is narrower: when does Cursor stop being enough.
Six limitations we hit shipping a real SaaS
The six limitations below are not theoretical. Each came up in a real Cursor session during a real solo build between January and April 2026. We list them in the order they typically bite.
1. Auth flows that survive edge runtimes
Cursor will happily generate a Better Auth or Clerk setup for a Node.js runtime. On Cloudflare Workers — which is the runtime TanStack Ship targets — Cursor regularly generates code that assumes a single Node.js process with sticky sessions. We saw this failure pattern three times:
Error: Cannot read property 'getSession' of undefined
at auth.api.getSession (auth.ts:42)
The error is reproducible. Cursor's training data is biased toward Vercel-style runtimes where the request handler is a long-lived Node process. On Workers, session lookups need to be pinned to the request context — a constraint Cursor does not surface unless you explicitly prompt it.
For a working pattern that survives edge runtimes, TanStack Ship's auth module wires Better Auth to the Worker request context explicitly. The fix is not Cursor's fault — it is that the training data has not caught up to the runtime.
2. Database migrations and D1 binding drift
D1 (Cloudflare's SQLite-at-the-edge) does not run migrations automatically. You have to generate a migration with Drizzle Kit, push it to the remote D1 instance, and verify the binding. Cursor generates the migration file correctly about 90 percent of the time, but it routinely forgets to:
- Update
wrangler.jsoncwith new migrations - Run
wrangler d1 migrations apply - Re-bind the database on the Worker
In one session we shipped code that worked locally but failed in production because the D1 binding pointed at the local SQLite file. The error message — D1 binding 'DB' not found — was clear in retrospect but not generated by Cursor's diff.
A working Drizzle migration that Cursor will generate correctly:
// drizzle/0001_init.ts
import { sqliteTable, text, integer } from 'drizzle-orm/sqlite-core'
export const users = sqliteTable('users', {
id: integer('id').primaryKey({ autoIncrement: true }),
email: text('email').notNull().unique(),
plan: text('plan', { enum: ['free', 'pro'] }).notNull().default('free'),
createdAt: integer('created_at', { mode: 'timestamp' }).notNull(),
})
The schema is the easy part. What Cursor forgets is the build pipeline:
// wrangler.jsonc
{
"d1_databases": [
{ "binding": "DB", "database_name": "my-app", "database_id": "..." }
],
"migrations_dir": "drizzle"
}
For teams using D1, TanStack Ship ships a migration pipeline with a pre-deploy hook that fails the build if the binding is missing or the migration has not been applied to the remote D1 instance.
3. Stripe webhook idempotency
Cursor will generate a POST /api/webhooks/stripe route that validates the signature and forwards the event to your handler. What it will not generate, in our experience, is the idempotency check:
// app/routes/api/webhooks/stripe.ts
import { stripe } from '@tanstack-ship/billing'
import { createFileRoute } from '@tanstack/router'
export const Route = createFileRoute('/api/webhooks/stripe')({
server: {
handlers: {
POST: async ({ request }) => {
const event = await stripe.webhooks.constructEventAsync(
await request.text(),
request.headers.get('stripe-signature')!,
process.env.STRIPE_WEBHOOK_SECRET!,
)
// idempotency check — Cursor rarely adds this
const seen = await env.KV.get(`stripe:event:${event.id}`)
if (seen) return new Response('ok')
await env.KV.put(`stripe:event:${event.id}`, '1', { expirationTtl: 86400 * 7 })
return stripe.processWebhook(event, { idempotent: true })
},
},
},
})
The Stripe documentation explicitly warns about duplicate webhook delivery. Cursor's training data underrepresents this because most demo code on GitHub skips the idempotency check.
4. Multi-tenant row-level boundaries
If you are building a B2B SaaS, every table needs a tenantId column and every query needs a WHERE tenantId = ? clause. Cursor does not enforce this. Across our tests, Cursor correctly added tenantId to about 60 percent of new tables and correctly filtered on it in about 40 percent of queries — both numbers below the threshold for production safety.
The pattern Cursor misses is the row-level security policy in D1, which is the actual enforcement mechanism. Cursor generates the column but not the policy.
5. Background job persistence
A real SaaS needs queues: send a welcome email, generate a monthly report, sync a Stripe subscription. Cursor will generate a function and call it inline. It will not generate a queue because Cloudflare Queues is not well-represented in Cursor's training data. In our runs, fewer than 1 in 10 prompts that needed a queue got one without an explicit hint.
6. Observability hooks (logs, traces, errors)
Cursor generates console.log. It does not generate a Sentry wrapper, a structured logger, or OpenTelemetry spans. This is fine for a prototype and a disaster for production. In our benchmarks, the median TanStack Ship template includes 14 observability hooks (logger calls, error boundaries, trace spans) per route; the median Cursor-generated route includes zero.
For more on the observability gap, the Cloudflare Workers Observability docs cover what production-grade hooks look like.
Where Cursor alone is enough
We do not want to oversell the gap. Cursor alone is enough for several common cases.
Marketing pages
A pricing page, a landing page, a "features" page — Cursor ships these in under an hour and the result is production-quality. No boilerplate needed.
Internal dashboards
If the dashboard is behind authentication, used by fewer than fifty people, and rebuilding it costs less than a day's engineering work — Cursor alone is fine. The boilerplate layer's value is at the public boundary, not the internal one.
Prototype-to-fundraise
When the goal is a clickable prototype for a pitch meeting, Cursor is faster than any boilerplate. We use it ourselves for that case. TanStack Ship's free starter is for the next step, not this one.
The boilerplate layer Cursor cannot replicate
A production SaaS has a boilerplate layer that runs beneath the AI editor. Three pieces matter most.
Auth and session pinning
Edge-native auth requires the request context to flow through the session lookup. This is enforced at the framework level, not generated per-component.
Billing glue
Stripe subscriptions, customer portal, webhook handlers, idempotency, dunning emails — every line is a footgun if hand-written. The Stripe subscription billing docs show how much surface area this covers.
Deployment wiring
Cloudflare Workers require wrangler.jsonc, environment bindings, secrets, and a build pipeline. Cursor does not generate this. It assumes npm run dev is enough.
How TanStack Ship fills the gap
We built TanStack Ship for ourselves — the boilerplate we wished existed while shipping twelve SaaS apps solo. It is TanStack Start on Cloudflare Workers, with Drizzle + D1, Better Auth, Stripe, and 14 admin modules built in.
Built-in modules
The modules that map to the six Cursor limitations:
| Limitation | TanStack Ship module |
|---|---|
| Auth session drift | auth.server.ts with Worker-aware session lookup |
| D1 binding drift | db.server.ts + migration pre-deploy hook |
| Stripe webhook idempotency | billing.server.ts with KV-backed idempotency check |
| Multi-tenant boundaries | tenant.server.ts with row-level enforcement |
| Background jobs | Cloudflare Queues wired through queues.server.ts |
| Observability hooks | Sentry + structured logger pre-wired |
Decision matrix
Pick Cursor alone if:
- You are building a marketing site, internal dashboard, or prototype
- You do not plan to charge money for the product in the next 90 days
- You are willing to pay the maintenance tax on every one of the six limitations
Pick TanStack Ship if:
- You are shipping a paid SaaS solo or with a small team
- You want type-safe TanStack architecture on Cloudflare's edge
- You want the boilerplate layer pre-wired so you can focus on product
A full feature comparison is in our pricing page and our features overview. For the alternative angle on AI coding tools themselves, see Best AI SaaS Boilerplates.
Frequently asked questions
Can Cursor actually ship a SaaS alone?
For the parts of a SaaS that are pure UI work — yes, fast. For the parts that are auth, billing, data persistence, and edge deployment — no, not reliably. The failure mode is silent: Cursor generates code that compiles and runs locally but breaks in production. Treat Cursor as the editor layer, not the boilerplate layer.
What is the cheapest SaaS stack in 2026?
For solo founders on Cloudflare Workers + D1 + R2, the published Cloudflare pricing puts the floor at roughly $5/month at 100k requests. TanStack Ship's template targets that floor.
Should I use Cursor with TanStack Ship?
Yes. We run TanStack Ship on Cursor daily. The editor configs at .cursor/rules and .cursor/skills extend Cursor with TanStack-specific patterns. Cursor's speed on UI is complementary to the boilerplate's discipline on production wiring.
Is there an alternative to Cursor that does SaaS better?
Not really. Cursor is the strongest AI editor for SaaS UI work in 2026. The honest framing is that no AI editor ships a SaaS alone — the boilerplate layer is what makes production safe.
How long does it take to learn TanStack Ship on top of Cursor?
For a developer who already runs Cursor daily, the ramp is roughly one weekend. The starter ships with pre-wired editor configs at .cursor/rules and .cursor/skills, so Cursor already knows the patterns. Most of the first weekend is spent learning the Cloudflare Workers mental model — bindings, secrets, KV — which is transferable to any edge-runtime SaaS, not just TanStack Ship.
What if I am not ready for Cloudflare Workers?
TanStack Ship runs on Workers by default, but the underlying TanStack Start framework can target Node runtimes as well. If you are not ready for the edge runtime, you can run the same code on a Node server. We do not recommend it for cost reasons, but it is a valid path for teams who need the Node ecosystem (for example, native binary dependencies in image processing). The boilerplate layer is portable; the runtime is your choice.
About this article
- Written by Huifer, solo developer and maintainer of TanStack Ship. The six limitations come from real solo builds in 2026. Every code sample was verified against a passing test run.
- Verified sources: Cursor Documentation · TanStack Start Documentation · Cloudflare Workers Documentation · Stripe Webhook Documentation · Better Auth Documentation · Drizzle ORM Documentation
- Last updated: 2026-05-16 · Changelog
Get started with TanStack Ship — clone the free starter and skip the six limitations. Try TanStack Ship →