Guides/ TypeScript

Next.js with Prisma and Better Auth

Scaffold a Next.js fullstack app with PostgreSQL, Prisma, Better Auth, tRPC, Tailwind CSS, and shadcn/ui, with clear server and database boundaries.

Updated July 30, 2026

nextjsprismabetter-authpostgres

This stack keeps UI, server routes, authentication, and application operations inside Next.js while using Prisma as the database access layer for PostgreSQL.

Next.js fullstack architecture with Prisma and Better Auth

bun create better-fullstack@latest my-next-app \
  --ecosystem typescript \
  --frontend next \
  --backend self \
  --runtime none \
  --database postgres \
  --orm prisma \
  --auth better-auth \
  --api trpc \
  --css-framework tailwind \
  --ui-library shadcn-ui \
  --shadcn-base radix \
  --shadcn-style nova \
  --shadcn-icon-library lucide \
  --shadcn-color-theme neutral \
  --shadcn-base-color neutral \
  --shadcn-font inter \
  --shadcn-radius default

What the generator owns

  • Next.js as both the React frontend and framework-owned server boundary.
  • Prisma schema and client wiring for PostgreSQL.
  • Better Auth integration on the server side.
  • tRPC procedures for typed application operations.
  • Tailwind CSS and shadcn/ui in the web app.
  • A reproducible configuration and generated workspace structure.

See the exact Next.js Prisma Better Auth template for its current command, parameters, and output inventory.

Keep Prisma server-only

React Server Components can make server and browser code look close together. The runtime boundary still matters. Keep Prisma client creation in server-only modules and expose use-case-shaped functions rather than importing the client broadly.

export async function getDashboardProjects(userId: string) {
  return prisma.project.findMany({
    where: { ownerId: userId },
    orderBy: { updatedAt: "desc" },
    select: { id: true, name: true, updatedAt: true },
  });
}

This query returns the fields the dashboard needs and makes ownership part of the query. It does not expose a generic findMany operation to the client.

Model auth relationships deliberately

Let Better Auth own session and credential details. Product tables should reference the stable user identifier.

model Project {
  id        String   @id @default(cuid())
  ownerId   String
  name      String
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt

  @@index([ownerId])
}

Whether you declare a direct relation to an auth-owned user model depends on the generated adapter schema and migration ownership. Avoid guessing; inspect the generated schema and decide which package owns changes.

Migration and deployment workflow

Treat schema generation and migration application as different steps. A safe deployment pipeline usually:

  1. installs the exact locked dependencies;
  2. generates the Prisma client;
  3. builds and typechecks the application;
  4. applies reviewed production migrations;
  5. starts the new application version.

Keep DATABASE_URL, auth secrets, and trusted callback origins out of public Next.js environment variables.

Prisma or Drizzle?

Choose based on preferred schema and query workflow.

PreferencePrismaDrizzle
Schema sourcePrisma schema languageTypeScript schema definitions
Query styleGenerated client APISQL-oriented TypeScript API
Migration workflowPrisma toolingDrizzle tooling
Better Fullstack recordPrisma templateDrizzle template

Neither choice removes the need for indexes, constraints, transactions, or reviewed migrations. For a longer decision framework, read Drizzle vs Prisma for a fullstack starter.

Compatibility notes

Next steps

Patreon