Engineering10 May 20268 min read

Building a Full-Stack App with Next.js 15 and Prisma

A deep dive into setting up a type-safe data layer with Prisma ORM and PostgreSQL inside a Next.js App Router project — pitfalls included.

Next.jsPrismaPostgreSQLTypeScript

When I set out to migrate my portfolio's project list from a static TypeScript array to a real database, I underestimated just how many sharp edges the Prisma 7 → Prisma 5 downgrade journey would involve. Here's what I learned.

Why Prisma?

Prisma provides an end-to-end type-safe ORM experience. The generated client mirrors your schema, so TypeScript catches mismatches at compile time — not at 2 AM in production.

The Generator Trap

Prisma 7 ships with a new prisma-client generator that requires either Prisma Accelerate or a driver adapter. If you're pointing straight at PostgreSQL (as most projects do), you'll hit a cryptic runtime error about accelerateUrl being undefined. The fix? Downgrade to Prisma 5 and use the battle-tested prisma-client-js generator.

generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
}

The Singleton Pattern

Next.js dev mode re-evaluates modules on every request in certain scenarios, which can exhaust your connection pool quickly. The fix is a global singleton:

const globalForPrisma = globalThis as unknown as { prisma: PrismaClient };
export const prisma =
  globalForPrisma.prisma ?? new PrismaClient({ log: ['error', 'warn'] });
if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = prisma;

Takeaways

  • Pin major Prisma versions in package.json until the ecosystem catches up.
  • Always run prisma migrate deploy in your build command on Render/Vercel.
  • Keep your seed file idempotent with upsert — never plain create.