Skip to content

About

boiler code for trpc

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

Next.js + tRPC + Prisma + Better Auth Starter

A production-oriented starter template / boilerplate for building type-safe, secure web apps without wiring the foundations from scratch every time. It ships with end-to-end type safety, database-backed authentication, role-based access control, and a clean, accessible UI kit.

Use it as the base for admin dashboards, internal tools, or SaaS products: clone it, rename it, and start building features on day one.


Highlights

  • End-to-end type safety from the database to the UI (Prisma -> tRPC -> React).
  • Email/password auth with database sessions (revocable), Argon2id hashing, and email verification.
  • Role-based access control (USER, MODERATOR, ADMIN, SUPER_ADMIN) enforced server-side on every procedure.
  • Built-in rate limiting, hardened security headers, and Zod-validated environment variables.
  • Dockerized PostgreSQL with migrations and seed data.
  • A small, accessible UI kit (forms, tables, dialogs) with loading/empty/ error states and a working example CRUD feature (Products).

Tech stack

Concern Choice
Framework Next.js 16 (Pages Router)
Language TypeScript (strict)
API tRPC 11
Data / ORM Prisma 6 + PostgreSQL 17
Auth Better Auth (email/password, DB sessions)
Password hashing Argon2id (@node-rs/argon2)
Validation Zod
Forms React Hook Form + @hookform/resolvers
Data fetching TanStack Query (via tRPC)
Styling Tailwind CSS 4
Email Nodemailer (console in dev, SMTP in prod)
Container Docker Compose

Why Better Auth instead of Auth.js/NextAuth? NextAuth's Credentials provider is JWT-only by design and cannot use database sessions, which rules out true server-side session revocation ("sign out everywhere"). Better Auth supports email/password on database sessions natively, with pluggable Argon2id, email verification, RBAC, rate limiting, and MFA/passkey plugins.


Getting started

Prerequisites

  • Node.js 20+
  • pnpm
  • Docker (for PostgreSQL)

1. Install dependencies

pnpm install

2. Configure environment

cp .env.example .env
# Then set a strong secret:
#   openssl rand -base64 32   ->   BETTER_AUTH_SECRET

3. Start the database

docker compose up -d

4. Apply migrations and seed data

pnpm db:migrate
pnpm db:seed

5. Run the app

pnpm dev
# http://localhost:3000  ->  redirects to /products (sign in required)

6. Create your first admin

Register an account at /auth/register. In development, the verification email is printed to the server console (no SMTP required) — open the logged link to verify.

New users get the USER role. To grant admin rights (so you can create/edit/ remove products), promote yourself:

docker exec trpc_testing_db \
  psql -U postgres -d trpc_testing \
  -c "UPDATE \"user\" SET role='ADMIN' WHERE email='you@example.com';"

Available scripts

Script Description
pnpm dev Start the dev server
pnpm build Generate Prisma client + production build
pnpm start Run the production build
pnpm lint Lint with ESLint
pnpm db:migrate Create/apply Prisma migrations (dev)
pnpm db:push Push schema without a migration
pnpm db:seed Seed demo categories and products
pnpm db:studio Open Prisma Studio

Project structure

prisma/
  schema.prisma          # Product/Category + Better Auth models
  migrations/            # SQL migrations
  seed.mjs               # Demo data
src/
  components/            # UI kit (ui.tsx), Layout, ConfirmDialog, ProductForm
  features/
    auth/                # AuthCard, RequireAuth guard, LogoutButton
  hooks/
    use-current-user.ts  # Client session/role helper (UX only)
  lib/
    auth.ts              # Better Auth server instance
    auth-client.ts       # Better Auth React client
    env.ts               # Zod-validated environment (server-only)
    password.ts          # Argon2id hash/verify
    permissions.ts       # Roles, hierarchy, access control (shared)
    product.ts           # Product schema/helpers
  pages/
    api/auth/[...all].ts # Better Auth catch-all handler
    api/trpc/[trpc].ts   # tRPC handler
    auth/                # login, register, verify-email
    products/            # list, new, [id]/edit
  schemas/
    auth.ts              # Zod auth schemas (login/register/password policy)
  server/
    db.ts                # Prisma client singleton
    context.ts           # tRPC context (resolves session server-side)
    trpc.ts              # public/protected/admin procedures + RBAC + errors
    routers/             # auth, product, category
    services/            # mailer, security logger
  styles/                # Tailwind globals + design tokens
  utils/
    trpc.ts              # Typed tRPC hooks

See PRODUCT.md and DESIGN.md for product intent and the design system.


Authentication and authorization

  • Sessions: database-backed (revocable), HttpOnly + SameSite=Lax cookies, Secure in production, with expiry and daily rotation.
  • Passwords: Argon2id (64 MiB / 3 iterations), never stored in plaintext.
  • Email verification: required before sign-in; reset/verify tokens are single-use and short-lived.
  • RBAC: USER < MODERATOR < ADMIN < SUPER_ADMIN. tRPC exposes publicProcedure, protectedProcedure, and adminProcedure; authorization is always enforced server-side (the client UI only hides affordances).
  • Rate limiting: enabled with database storage, with stricter limits on sign-in, sign-up, verification, and password reset.

Adding a protected endpoint

// src/server/routers/example.ts
import { z } from "zod";
import { protectedProcedure, adminProcedure, router } from "../trpc";

export const exampleRouter = router({
  mine: protectedProcedure.query(({ ctx }) => ctx.user.id),
  adminOnly: adminProcedure
    .input(z.object({ value: z.string() }))
    .mutation(({ input }) => input.value),
});

Register it in src/server/routers/_app.ts.


Security posture

Designed against OWASP ASVS Level 2 and the OWASP Top 10:

  • Broken Access Control -> server-side RBAC + session checks on every mutation.
  • Cryptographic Failures -> Argon2id, secure cookies, secrets from env only.
  • Injection -> Prisma parameterized queries only (no raw SQL).
  • Security Misconfiguration -> CSP, HSTS, X-Content-Type-Options, Referrer-Policy, Permissions-Policy, X-Frame-Options (see next.config.ts).
  • Identification & Auth Failures -> password policy, email verification, revocable sessions, rate limiting.
  • CSRF -> SameSite=Lax cookies + same-origin tRPC; Better Auth validates request origin for state-changing calls.

Errors returned to clients are generic; internal details (Prisma/stack traces) are logged server-side only.


Roadmap

This template ships Phase 1 (core auth + RBAC + hardening). The auth architecture is intentionally MFA- and passkey-ready:

  • Phase 2: forgot/reset password, change password, change email, delete account, list sessions + revoke one / revoke all ("sign out everywhere").
  • Phase 3: MFA/2FA (TOTP) and passkeys (WebAuthn).

License

Provided as a starter template — adapt freely for your own projects.

About

boiler code for trpc

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages