Full-Stack SaaS Architecture With Next.js and Node.js

How I structure a production SaaS from scratch — folder layout, API design, auth, database modelling, and deployment pipeline — using Next.js and Node.js.

I have set up the same SaaS foundation many times across different products. The stack choices vary slightly, but the structural decisions are consistent. This is the architecture I start with when building a new full-stack SaaS product with Next.js on the frontend and Node.js on the backend.

The Stack

Folder Structure

The Next.js frontend follows a feature-based structure rather than type-based:

src/
  app/                    # Next.js App Router pages
    (auth)/               # Route group — auth pages
    (dashboard)/          # Route group — app pages
    api/                  # API routes (lightweight, proxy to backend)
  components/
    ui/                   # Generic reusable components
    [feature]/            # Feature-specific components
  lib/
    api.ts                # Typed API client
    auth.ts               # Auth helpers
    utils.ts
  hooks/                  # Custom hooks
  stores/                 # Zustand stores

The Node.js backend mirrors this with a layered structure:

src/
  routes/                 # Express routers — HTTP only
  services/               # Business logic
  repositories/           # Database queries
  middleware/             # Auth, validation, rate limiting
  lib/
    db.ts                 # Prisma client singleton
    redis.ts              # Redis client
  types/                  # Shared TypeScript types

Authentication

For most SaaS products, I implement email/password auth plus Google OAuth as a minimum. The pattern I use:

If the product is NextJS-only (no separate backend API), NextAuth.js handles most of this cleanly. When there is a separate Node.js API, custom JWT implementation gives more control and avoids the coupling to the Next.js deployment.

Multi-Tenancy

Most SaaS products need some concept of organisations or workspaces. The database pattern I use is row-level tenancy — every tenant-scoped table has an organisation_idcolumn, and every query includes a tenant filter.

The important part: enforce this at the service layer with a tenant context object, not just at the route level. If the current user's organisation ID is not threaded through every data access call, you will eventually ship a data leak. I have seen this happen.

Subscription and Billing

Stripe is the only reasonable choice here. The integration that works well in production:

Deployment Pipeline

The setup I use for most products:

What I Skip in the Early Stage

Microservices, message queues, and event-driven architecture are real tools for real problems. They are not good starting points for an early-stage SaaS. A well-structured monolith with clean boundaries is faster to build, easier to debug, and straightforward to split later if a specific component genuinely needs to scale independently.

If you are building a SaaS product and want to talk through the architecture, I am happy to help.