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.
| Layer | Choice | Why |
|---|---|---|
| Framework | Next.js (App Router) | File-system routing, RSC, edge-ready |
| Auth | Auth.js v5 | Sessions, OAuth, Credentials in one library |
| Database | Postgres + Prisma | Type-safe queries, real migrations |
| React Email + Resend | Components you can preview in a browser | |
| Payments | Stripe | Webhooks, customer portal, subscriptions |
| Hosting | Vercel | Zero-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.
How auth flows from click to cookie
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:
- Add a billing flow — Stripe Checkout, a webhook handler for
checkout.session.completed, and a customer portal link in Settings. - Add an admin panel — a route group behind
UserRole.ADMIN, a user table, and a basic audit log. - Add your core feature — the thing only your product does. The boring foundation is there so you can focus on this.
- Add observability — Sentry for errors, Posthog for analytics, a
logstable 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.