Skip to content
Norcel
Back to blog
Demo · · 4 min read · Norcel

Demo Blog: A Walkthrough of the Norcel Stack

A hands-on demo showing how Norcel's Next.js SaaS starter handles auth, database, email, and billing — from zero to production in a weekend.

Welcome to the Norcel demo blog. If you’ve landed here, you’re probably either evaluating the starter or wondering what “production-ready” looks like in practice. This post walks through the full stack — what each piece does, why it’s there, and how you’d swap it out if you needed to.

What you get out of the box

The starter ships with a small number of carefully chosen pieces. Nothing is included for the sake of completeness — everything earns its place.

LayerChoiceWhy
FrameworkNext.js (App Router)File-system routing, RSC, edge-ready
AuthAuth.js v5Sessions, OAuth, Credentials in one library
DatabasePostgres + PrismaType-safe queries, real migrations
EmailReact Email + ResendComponents you can preview in a browser
PaymentsStripeWebhooks, customer portal, subscriptions
HostingVercelZero-config deploys from git push

No build tool configuration. No webpack plugins. No ejecting anything. You open the repo, run npm install, copy .env.example to .env.local, and it boots.

The first five minutes

Clone the repo, install dependencies, and start the dev server:

git clone https://github.com/norcel/starter your-app
cd your-app
npm install
cp .env.example .env.local
npm run dev

Open http://localhost:3000. You’ll see the marketing landing page. Navigate to /login — the form is wired to a real Credentials provider backed by Argon2id hashing and a user_sessions table in Postgres.

No mocks. No in-memory stores. Dev runs against a real Postgres instance, which is why the prod and dev shapes are identical.

When a user submits the login form, here’s the exact path the request takes:

User → POST /api/auth/callback/credentials
  → Auth.js Credentials.authorize()
    → Zod parse (fail fast)
    → prisma.user.findUnique()
    → verifyPassword() (Argon2id)
    → startUserSession() → INSERT user_sessions
    → return { id, email, sessionId }
  → Auth.js signs JWT (payload: id, email, sessionId)
  → Set-Cookie: HttpOnly, Secure, SameSite=Lax

The cookie holds a signed JWT. The JWT holds a sessionId. On every request, middleware fetches that session row from Postgres and checks it’s still valid. If a user clicks “log out everywhere”, the row is deleted — every device is logged out immediately.

This is the pattern that makes revocation cheap and fast.

Database in 60 seconds

The schema is small by design. You’ll grow it for your product, not inherit a schema shaped by someone else’s product.

model User {
  id            String         @id @default(cuid())
  email         String         @unique
  passwordHash  String?
  emailVerified DateTime?
  role          UserRole       @default(USER)
  createdAt     DateTime       @default(now())
  sessions      UserSession[]
  accounts      Account[]
}

model UserSession {
  id        String   @id @default(cuid())
  userId    String
  expiresAt DateTime
  lastSeen  DateTime @default(now())
  user      User     @relation(fields: [userId], references: [id], onDelete: Cascade)
}

Run npx prisma migrate dev --name init to apply it. Prisma generates a typed client from this schema — every prisma.user.findUnique() call is checked against the actual columns.

Sending email

The welcome email is a React component. You preview it in the browser during development:

// emails/welcome.tsx
import { Html, Button, Text } from '@react-email/components';

export default function WelcomeEmail({ name }: { name: string }) {
  return (
    <Html>
      <Text>Hi {name}, welcome to Norcel.</Text>
      <Button href="https://app.norcel.com/dashboard">
        Open your dashboard
      </Button>
    </Html>
  );
}

Run npx email dev and you’ll see a browser preview of every template with hot reload. When you send via Resend, the HTML rendered by @react-email/render is exactly what appears in the user’s inbox.

No more pushing code to staging to see if the email looks right.

What’s next

This is the foundation. The actual product — the thing your users pay for — goes on top of this. Some common next steps:

  1. Add a billing flow — Stripe Checkout, a webhook handler for checkout.session.completed, and a customer portal link in Settings.
  2. Add an admin panel — a route group behind UserRole.ADMIN, a user table, and a basic audit log.
  3. Add your core feature — the thing only your product does. The boring foundation is there so you can focus on this.
  4. Add observability — Sentry for errors, Posthog for analytics, a logs table for auth events.

The starter doesn’t try to give you all of this. It gives you the minimum foundation that every SaaS needs, built correctly, so you can focus on the part that’s actually unique to your product.

The philosophy in one sentence

Ship the boring parts right so you can ship the interesting parts fast.

That’s Norcel. The demo is live at norcel.com. The starter is on GitHub. Everything in this post is real code you can read.

Keep reading

Demo ·

Demo Blog 4: Scaling Edge Middleware & Background Jobs

A deep dive into building low-latency edge middlewares, handling async workloads, and keeping background workers rock solid in Next.js.

Architecture ·

Norcel OS: Treating Your SaaS Starter as an Operating System

A deep dive into the Norcel architecture philosophy — how treating your Next.js SaaS starter as an OS-level foundation changes the way you ship products.

Next.js ·

How to Build a Production-Ready SaaS with Next.js

A practical walkthrough of the architecture, security, and infrastructure decisions that turn a Next.js prototype into a SaaS you can charge for.