How to create Next.js, Postgres application
Below is a step-by-step, production-ready guide to get started with a Next.js + PostgreSQL + Prisma stack.
1. Create a Next.js application
npx create-next-app@latest nextjs-prisma-postgres ? Would you like to use the recommended Next.js defaults? › - Use arrow-keys. Return to submit. ❯ Yes, use recommended defaults - TypeScript, ESLint, Tailwind CSS, App Router No, reuse previous settings No, customize settings # select recommended default as an option cd nextjs-prisma-postgres # list of folders ls -la total 512 drwxr-xr-x@ 16 techtrekk staff 512 8 Feb 11:42 . drwx------@ 7 techtrekk staff 224 8 Feb 11:42 .. drwxr-xr-x@ 12 techtrekk staff 384 8 Feb 11:42 .git -rw-r--r--@ 1 techtrekk staff 480 8 Feb 11:42 .gitignore drwxr-xr-x@ 4 techtrekk staff 128 8 Feb 11:43 .next drwxr-xr-x@ 6 techtrekk staff 192 8 Feb 11:42 app -rw-r--r--@ 1 techtrekk staff 465 8 Feb 11:42 eslint.config.mjs -rw-r--r--@ 1 techtrekk staff 251 8 Feb 11:43 next-env.d.ts -rw-r--r--@ 1 techtrekk staff 133 8 Feb 11:42 next.config.ts drwxr-xr-x@ 291 techtrekk staff 9312 8 Feb 11:42 node_modules -rw-r--r--@ 1 techtrekk staff 227096 8 Feb 11:42 package-lock.json -rw-r--r--@ 1 techtrekk staff 544 8 Feb 11:42 package.json -rw-r--r--@ 1 techtrekk staff 94 8 Feb 11:42 postcss.config.mjs drwxr-xr-x@ 7 techtrekk staff 224 8 Feb 11:42 public -rw-r--r--@ 1 techtrekk staff 1450 8 Feb 11:42 README.md -rw-r--r--@ 1 techtrekk staff 666 8 Feb 11:42 tsconfig.json
Now let us run server locally
npm run dev > nextjs-prisma-postgres@0.1.0 dev > next dev ▲ Next.js 16.1.6 (Turbopack) - Local: http://localhost:3000 - Network: http://169.254.115.48:3000 ✓ Starting... ✓ Ready in 275ms GET / 200 in 1047ms (compile: 990ms, render: 57ms)
You now have Next.js running locally.
Refer to post How to setup PostgreSQL on Ubuntu server for installation of Postgres and creation of database and database user required for new application.
3. Add Prisma to Next.js
npm install prisma --save-dev npm install @prisma/client npx prisma init
This creates:
prisma/
schema.prisma
.env
You are now using Prisma.
4. Configure PostgreSQL connection
create / Edit .env to include DATABASE_URL:
DATABASE_URL="postgresql://username:password@localhost:5432/mydb?schema=public"
Replace username, password, and mydb with your actual PostgreSQL credentials and database name.
Edit prisma/schema.prisma to include your model
datasource db { provider = "postgresql" url = env("DATABASE_URL") } generator client { provider = "prisma-client-js" binaryTargets = ["native", "debian-openssl-3.0.x"] } model User { id String @id @default(cuid()) name String? email String @unique password String createdAt DateTime @default(now()) updatedAt DateTime @updatedAt posts Post[] } model Post { id String @id @default(cuid()) title String content String excerpt String? slug String @unique published Boolean @default(false) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt authorId String comments Comment[] author User @relation(fields: [authorId], references: [id]) }
ensure you add binaryTargets as you might compile on MacOS but server might be on Ubuntu
5. Run migration
npx prisma migrate dev --name init
✔ Creates tables ✔ Generates Prisma Client ✔ Syncs schema with DB
Generate Prisma Client Generate the Prisma Client (this happens automatically with the migration, but you can also run it manually):
npx prisma generate
6. Prisma client singleton (VERY IMPORTANT)
Create lib/prisma.ts:
import { PrismaClient } from "@prisma/client"; const globalForPrisma = global as unknown as { prisma: PrismaClient | undefined; }; export const prisma = globalForPrisma.prisma ?? new PrismaClient({ log: ["query"], }); if (process.env.NODE_ENV !== "production") { globalForPrisma.prisma = prisma; }
Prevents connection exhaustion during hot reloads.
7. Use Prisma in Route Handlers (API)
app/api/posts/route.ts
import { prisma } from "@/lib/prisma"; import { NextResponse } from "next/server"; export async function GET() { const posts = await prisma.post.findMany(); return NextResponse.json(posts); } export async function POST(req: Request) { const body = await req.json(); const post = await prisma.post.create({ data: { title: body.title, content: body.content, }, }); return NextResponse.json(post); }
8. Use Prisma in Server Components (recommended)
import { prisma } from "@/lib/prisma"; export default async function Page() { const posts = await prisma.post.findMany(); return ( <ul> {posts.map((p) => ( <li key={p.id}>{p.title}</li> ))} </ul> ); }
✔ No API calls ✔ Faster ✔ Secure (server-only)
9. Prisma Studio (DB UI)
# Open Prisma Studio to view/edit your data npx prisma studio # Create and apply migrations npx prisma migrate dev # Push schema changes without migrations (for prototyping) npx prisma db push # Regenerate Prisma Client after schema changes npx prisma generate # for running existing database npx prisma db pull
Gives a web UI to inspect/edit your database.
10. Project structure (clean & scalable)
src/
├─ app/
│ ├─ api/
│ │ └─ posts/
│ └─ page.tsx
├─ lib/
│ ├─ prisma.ts
├─ prisma/
│ └─ schema.prisma
11. Common mistakes (avoid these)
❌ Using Prisma in Client Components
❌ Creating PrismaClient per request
❌ Skipping migrations in prod
❌ Hardcoding DB credentials
❌ Running migrate dev in production
12. Best practices
✅ Use Server Components for reads
✅ Use Route Handlers for writes
✅ Add indexes in Prisma schema
✅ Use select / include explicitly
✅ Version control migrations