The Landscape in 2025
Building a SaaS product on Next.js today is meaningfully different than it was two years ago. The App Router has matured, React Server Components are the default pattern rather than an experiment, and the ecosystem around Next.js — Neon for Postgres, Upstash for Redis, Clerk for auth — has caught up to what the framework enables.
After building RoofMarshal and several client projects on this stack, here are the decisions I've settled on.
The Stack
Framework: Next.js 15 (App Router)
Language: TypeScript (strict mode)
UI: Tailwind CSS + shadcn/ui
Database: Neon (serverless Postgres)
ORM: Prisma
Auth: Clerk
Email: Resend
Queue: Upstash QStash
Cache: Upstash Redis
AI: Anthropic Claude API
Deployment: VercelThis isn't the only valid stack. It's the one I've found to have the best developer experience, the lowest ops burden, and the best cold-start performance on Vercel's infrastructure.
The Data Layer
**Use Prisma with Neon, not raw SQL.** Prisma's type safety catches more bugs at compile time than any runtime validation strategy. The Neon serverless driver performs well on Vercel's edge and handles connection pooling out of the box.
The one thing Prisma gets wrong: migrations during CI. Use `prisma migrate deploy` in production, never `migrate dev`. And generate the client as part of your build step, not as a pre-push hook.
**Row-level security from day one.** If you're building multi-tenant SaaS, add tenant isolation to every query before you write your first real feature. Retrofitting RLS is painful. It means auditing every query in the codebase and hoping you didn't miss anything.
// Every query includes tenant context
async function getLeads(tenantId: string) {
return prisma.lead.findMany({
where: { tenantId }, // This should never be optional
});
}Server Actions vs API Routes
Use Server Actions for mutations that originate from the UI. Use API Routes for anything that needs to be called from outside the Next.js context — webhooks, mobile apps, external services.
The ergonomics of Server Actions are genuinely good: you get automatic request/response typing, built-in CSRF protection, and easy integration with React's transition APIs for optimistic updates.
What Server Actions don't do well: streaming responses, long-running operations, and anything that needs fine-grained HTTP control. Those belong in Route Handlers.
The Auth Decision
Clerk. Not because it's the only option, but because auth is not a place to be clever. Clerk handles sessions, JWTs, multi-factor, OAuth, and organization/tenant management in a way that would take weeks to build correctly from scratch.
The one friction point: if you later want to self-host auth (for compliance or cost reasons), migrating away from Clerk is non-trivial. Make that call at the start, not after you have thousands of users.
What I'd Do Differently
**Typed server errors from the start.** Most Next.js apps throw untyped errors from Server Actions and let the client figure it out. I now define explicit error types:
type ServerResult<T> =
| { success: true; data: T }
| { success: false; error: string; code: string };Every Server Action returns this shape. The client never has to guess whether an error is a network failure, a validation error, or a business logic error.
**Feature flags on day one.** Shipping a feature to 100% of users immediately is the riskiest thing you can do in a SaaS product. I now wire up a simple flag system (Upstash Redis or a DB-backed table) before building any feature, so I can roll out gradually and kill a bad release without a deploy.
**Audit logs as a first-class concern.** For anything that touches sensitive data or user actions, add audit logging before the feature ships. Users will ask "who changed this?" It's much easier to have the answer.
The Deployment Setup
Vercel for hosting. GitHub Actions for CI (run tests, type check, lint before merge). Neon branch databases for preview deployments — every Vercel preview gets its own Neon branch, seeded from production snapshots.
This setup means every PR is testable against real data shapes, and merging to main is the only deploy that touches production.
What's Next
The biggest shift I see coming is AI becoming an architectural concern, not a feature concern. Right now most teams add AI as an enhancement — smart suggestions, content generation, lead scoring. The next generation of SaaS will have AI embedded in the data model itself, shaping what gets stored, how it's retrieved, and how decisions get made.
Building that well requires the same discipline as the rest of the stack: typed interfaces, explicit failure modes, and a clear separation of concerns. The teams that get there first won't be the ones who moved fastest on demos — they'll be the ones who treated AI integration with the same engineering rigor as everything else.