Tech1 views

Part 2: Build Your First Prisma Project from Scratch with PostgreSQL

In the first article, we got to know Prisma from a macro perspective. Now, in this one, we'll start directly from a real project: using Prisma 7 + Next.js 16.2.2 + PostgreSQL + pnpm, from an empty directory to "being able to see the first User record from the database on the page".

You will complete:


Complete Command Overview (Quick Reference)

Here's a quick look at all the key commands used in this article:

text
# 1. Confirm / Install pnpm
corepack enable
corepack prepare pnpm@latest --activate

# 2. Create Next.js 16.2.2 project
pnpm create next-app@16.2.2 prisma-next-demo

cd prisma-next-demo

# 3. Install Prisma 7 + PostgreSQL dependencies
pnpm add -D prisma tsx @types/node @types/pg
pnpm add @prisma/client @prisma/adapter-pg pg dotenv

# 4. Initialize Prisma 7
pnpm prisma init

# 5. Generate and apply the first migration based on schema
pnpm prisma migrate dev --name init

# 6. Check migration status (optional)
pnpm prisma migrate status

# 7. Open Prisma Studio (visual data insertion)
pnpm prisma studio

# 8. Start Next.js development server
pnpm dev

The main text will explain step by step what each command does.


1. Prepare Environment: Node.js, pnpm, PostgreSQL

1.1 Node.js and pnpm

Make sure you have Node.js installed (v18+ recommended), then enable pnpm via Corepack:

text
corepack enable
corepack prepare pnpm@latest --activate
pnpm -v

If you see a version number, pnpm is ready to use.

1.2 Prepare PostgreSQL

You can provide PostgreSQL in any way. Here's a common Docker example:

text
docker run -itd \
  -e POSTGRES_USER=prisma \
  -e POSTGRES_PASSWORD=prisma \
  -e POSTGRES_DB=next_prisma_demo \
  -p 5432:5432 \
  --name next-prisma-postgres \
  postgres

Our conventions:

The .env DATABASE_URL will be:

text
DATABASE_URL="postgresql://prisma:prisma@localhost:5432/next_prisma_demo?schema=public"

If you're using a cloud PostgreSQL (Neon / Supabase / Railway / Prisma Postgres), simply replace the connection string.


2. Create Next.js 16.2.2 Project with pnpm

In your prepared directory, run:

text
pnpm create next-app@16.2.2 prisma-next-demo
cd prisma-next-demo

In the interactive guide, it's recommended to select:

After this step, you have a Next.js 16.2.2 App Router project skeleton.


3. Install Prisma 7 and PostgreSQL Dependencies

According to the official Prisma 7 Postgres Quickstart, we need: CLI + Client + PostgreSQL adapter + driver.

Run in the project:

text
pnpm add -D prisma tsx @types/node @types/pg
pnpm add @prisma/client @prisma/adapter-pg pg dotenv

Role explanation:


4. Initialize Prisma 7: Generate Config and Schema

Prisma 7 recommends using prisma init to generate a set of default configurations including prisma.config.ts.

Run in the project root:

text
pnpm prisma init

After execution, you'll see these new files:

These three files handle:

4.1 Configure .env DATABASE_URL

Open .env and fill in the connection string based on the PostgreSQL you prepared:

text
DATABASE_URL="postgresql://prisma:prisma@localhost:5432/next_prisma_demo?schema=public"

If using Prisma Postgres, fill in the PostgreSQL connection URL provided in the Prisma console. Make sure it's in the postgresql:// format.

4.2 Configure prisma.config.ts

The configuration method given in the official Prisma Next.js guide is roughly as follows:

text
// prisma.config.ts
import 'dotenv/config'
import { defineConfig, env } from 'prisma/config'

export default defineConfig({
  schema: 'prisma/schema.prisma',
  migrations: {
    path: 'prisma/migrations',
  },
  datasource: {
    url: env('DATABASE_URL'),
  },
})

This configuration explains:


5. Write the First Prisma Schema: User Model

Open prisma/schema.prisma and change it to a minimal usable version:

text
// prisma/schema.prisma

generator client {
  provider = "prisma-client"
  output   = "../generated/prisma"
}

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

model User {
  id        Int      @id @default(autoincrement())
  name      String
  email     String   @unique
  createdAt DateTime @default(now())
}

A few points here:

All subsequent Prisma Client types and safety features originate from this schema.


6. Generate and Apply the First Migration (Development)

The official Prisma recommended workflow is: use migrate dev for development, and migrate deploy for production.

Run in the project root:

text
pnpm prisma migrate dev --name init

It will:

If successful, you can optionally check the migration status:

text
pnpm prisma migrate status

It will show which migrations have been applied and whether the current database is in sync with the schema.

Production reminder (will be covered in detail later):
Using migrate dev in development is fine, but in production, only use migrate deploy and put it into your CI/CD pipeline, not manually on the server.


7. Create a Prisma Client Singleton in Next.js

To use Prisma Client safely in Next.js (especially with hot reloading in development), the official Next.js guide recommends using a singleton pattern with an adapter.

Create a new file lib/prisma.ts in the project:

text
// lib/prisma.ts
import { PrismaClient } from '../generated/prisma'
import { PrismaPg } from '@prisma/adapter-pg'

const connectionString = process.env.DATABASE_URL
if (!connectionString) {
  throw new Error('DATABASE_URL is not set')
}

const adapter = new PrismaPg({ connectionString })

const globalForPrisma = globalThis as unknown as {
  prisma?: PrismaClient
}

export const prisma =
  globalForPrisma.prisma ??
  new PrismaClient({
    adapter,
  })

if (process.env.NODE_ENV !== 'production') {
  globalForPrisma.prisma = prisma
}

This code illustrates several key concepts:

Throughout the project, you only need to import prisma from lib/prisma without creating a new instance again.


8. First Database Query on the Next.js Home Page

Next, we'll use Prisma to query the User table in app/page.tsx and render the results on the page.

Edit app/page.tsx:

text
// app/page.tsx
import { prisma } from '@/lib/prisma'

export default async function Home() {
  const users = await prisma.user.findMany({
    orderBy: { createdAt: 'desc' },
  })

  return (
    <main style={{ padding: '32px' }}>
      <h1>Prisma 7 + Next.js 16.2.2 + PostgreSQL</h1>
      <p>Current number of users: {users.length}</p>

      <ul>
        {users.map((u) => (
          <li key={u.id}>
            {u.name} - {u.email}
          </li>
        ))}
      </ul>
    </main>
  )
}

Since this is a Server Component (default), we can directly use await prisma.user.findMany in the function body. This is a very natural combination of Next.js App Router and Prisma.

The only problem now is that there is no user data in the database. We'll add one right away.


9. Insert the First User with Prisma Studio

Prisma Studio is the official Web UI to view and edit data directly in the browser.

Run in the project root:

text
pnpm prisma studio

A browser interface will open. On the left side, you'll see the User table:

  1. Click User;

  2. Click "Add Record";

  3. Fill in, for example:

  1. Save.

Back in the terminal, start the Next.js development server:

text
pnpm dev

Open your browser and go to http://localhost:3000. You should see:

At this point, the full chain is working: Browser → Next.js App Router → Prisma Client → PostgreSQL → Return Data → Page Render.


10. Example package.json (Prisma + Next.js + pnpm)

For your reference, here is a typical package.json snippet (only showing relevant parts from this article). You can adjust as needed:

text
{
  "name": "prisma-next-demo",
  "private": true,
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "next lint",
    "prisma:generate": "prisma generate",
    "prisma:migrate": "prisma migrate dev",
    "prisma:deploy": "prisma migrate deploy",
    "prisma:studio": "prisma studio"
  },
  "dependencies": {
    "@prisma/adapter-pg": "^7.0.0",
    "@prisma/client": "^7.0.0",
    "dotenv": "^16.0.0",
    "next": "16.2.2",
    "pg": "^8.0.0",
    "react": "18.3.1",
    "react-dom": "18.3.1"
  },
  "devDependencies": {
    "@types/node": "^22.0.0",
    "@types/pg": "^8.0.0",
    "prisma": "^7.0.0",
    "tsx": "^4.0.0",
    "typescript": "^5.0.0"
  }
}

11. Common Errors and Troubleshooting

11.1 Error: P1001 Cannot connect to database

Typical errors contain "The database server was reached but timed out" or "Cannot reach database server". Common causes:

Troubleshooting suggestions:

11.2 Error: DATABASE_URL is not set

In lib/prisma.ts, we explicitly check for process.env.DATABASE_URL and throw an error if missing.

Common causes:

Troubleshooting suggestions:

11.3 relation "User" does not exist

This means the table does not exist in the database, but you have already called a query. Common cause: forgot to run migrate dev or migrate deploy.

Solution:

text
pnpm prisma migrate dev --name init

Then restart pnpm dev.

11.4 Next.js Development: Too many connections / Warnings

If you frequently see Postgres errors like "too many clients already" during development, it's usually because PrismaClient is being instantiated multiple times, leading to connection pool accumulation.

Solution:


12. Summary: After This Article, You Have an "Extensible" Project Starting Point

In this article, we deliberately set up Prisma from scratch within the Prisma 7 + Next.js 16.2.2 + pnpm + PostgreSQL stack, rather than in a standalone script project. This ensures that all future practical articles (Next.js, MCP, AI-assisted development) can connect seamlessly.

By now, you have:

Reference Links

Prisma Documentation

  1. https://www.prisma.io/docs/guides/nextjs

  2. https://www.prisma.io/docs/guides/frameworks/nextjs

  3. https://www.prisma.io/docs/getting-started/prisma-orm/quickstart/postgresql

  4. https://www.prisma.io/docs/cli/migrate/deploy

  5. https://www.prisma.io/docs/guides/use-prisma-in-pnpm-workspaces

  6. https://www.prisma.io/docs/orm/prisma-migrate/workflows/development-and-production

GitHub

  1. https://github.com/prisma/prisma/discussions/24571

SHARE

Share

Share this article.