Isaac.

Using Prisma with PostgreSQL

Learn how to use Prisma ORM with PostgreSQL for type-safe database access and migrations.

By EMEPublished: February 20, 2025
prismapostgresqlormnode.jstypescriptdatabase

A Simple Analogy

Imagine a translator between you and a foreign speaker. You speak English, they speak Spanish, but neither of you speaks the other's language. Prisma is that translator—you write code in JavaScript/TypeScript, and Prisma translates it into SQL that PostgreSQL understands. You never have to write raw SQL; Prisma handles the conversation.


What Is Prisma?

Prisma is an ORM (Object-Relational Mapping) toolkit for Node.js and TypeScript that provides a modern way to access databases. It handles queries, migrations, type safety, and relationships—all with less boilerplate than traditional SQL.


Why Use Prisma with PostgreSQL?

  • Type safety: Full TypeScript support with auto-generated types
  • Simple syntax: Intuitive query API (easier than SQL)
  • Migrations: Easy schema management with auto-generated migration files
  • Relationships: Define one-to-many, many-to-many easily
  • Developer experience: Auto-complete and error checking in your IDE
  • No SQL strings: Never write raw SQL unless you need to

Getting Started with Prisma and PostgreSQL

1. Install Prisma

npm install @prisma/client
npm install -D prisma

2. Initialize Prisma

npx prisma init

This creates .env and prisma/schema.prisma files.

3. Configure Database Connection

.env:

DATABASE_URL="postgresql://user:password@localhost:5432/mydb"

4. Define Your Schema

prisma/schema.prisma:

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

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

model User {
  id    Int     @id @default(autoincrement())
  email String  @unique
  name  String?
  posts Post[]
}

model Post {
  id    Int     @id @default(autoincrement())
  title String
  body  String?
  published Boolean @default(false)
  author User    @relation(fields: [authorId], references: [id])
  authorId Int
}

5. Run Migrations

npx prisma migrate dev --name init

This creates the tables in PostgreSQL and generates the Prisma client.


Querying with Prisma

Create

const newUser = await prisma.user.create({
  data: {
    email: "alice@example.com",
    name: "Alice",
  },
});

Read

// Get one user
const user = await prisma.user.findUnique({
  where: { email: "alice@example.com" },
});

// Get all users
const allUsers = await prisma.user.findMany();

// Get with relations
const userWithPosts = await prisma.user.findUnique({
  where: { id: 1 },
  include: { posts: true },
});

Update

const updated = await prisma.user.update({
  where: { id: 1 },
  data: { name: "Alice Updated" },
});

Delete

const deleted = await prisma.user.delete({
  where: { id: 1 },
});

Relationships

One-to-Many

model Author {
  id    Int     @id @default(autoincrement())
  name  String
  books Book[]
}

model Book {
  id       Int     @id @default(autoincrement())
  title    String
  author   Author  @relation(fields: [authorId], references: [id])
  authorId Int
}

Many-to-Many

model Student {
  id       Int      @id @default(autoincrement())
  name     String
  courses  Course[]
}

model Course {
  id       Int      @id @default(autoincrement())
  name     String
  students Student[]
}

Practical Examples

Blog Application

// Create post with author
const post = await prisma.post.create({
  data: {
    title: "My First Post",
    body: "Content here...",
    author: { connect: { id: 1 } },
  },
});

// Find posts by author
const userPosts = await prisma.post.findMany({
  where: { authorId: 1 },
  include: { author: true },
});

E-commerce Order System

// Create order with items
const order = await prisma.order.create({
  data: {
    userId: 1,
    items: {
      create: [
        { productId: 1, quantity: 2 },
        { productId: 2, quantity: 1 },
      ],
    },
  },
});

// Get order with items
const orderDetail = await prisma.order.findUnique({
  where: { id: 1 },
  include: { items: { include: { product: true } } },
});

Advanced Features

Transactions

const result = await prisma.$transaction([
  prisma.user.update({ where: { id: 1 }, data: { balance: 100 } }),
  prisma.user.update({ where: { id: 2 }, data: { balance: 50 } }),
]);

Raw SQL (When Needed)

const result = await prisma.$queryRaw`
  SELECT * FROM "User" WHERE email = ${email}
`;

Aggregation

const count = await prisma.user.count();

const grouped = await prisma.post.groupBy({
  by: ["authorId"],
  _count: { id: true },
});

Real-World Use Cases

  • E-commerce: Product catalogs, orders, payments
  • Social media: Users, posts, comments, likes
  • SaaS: Multi-tenant apps with user and workspace data
  • Content management: Articles, categories, tags
  • IoT: Device data, sensor readings, logs

Best Practices

  • Use migrations for schema changes (never edit database directly)
  • Keep sensitive data out of .env (use secrets manager in production)
  • Use transactions for multi-step operations
  • Index fields that are frequently queried
  • Use select to only fetch needed columns
  • Implement soft deletes for important data
  • Monitor query performance with Prisma Studio
  • Use prisma studio command to browse data visually

Related Concepts to Explore

  • Entity-Relationship (ER) modeling
  • Database normalization
  • SQL and query optimization
  • Connection pooling
  • Database transactions and ACID
  • GraphQL with Prisma
  • Next.js integration with Prisma
  • TypeORM (alternative ORM)
  • Migration strategies
  • Database monitoring and scaling

Summary

Prisma makes working with PostgreSQL intuitive and type-safe. Instead of writing raw SQL or struggling with manual mapping, you define your schema once and get a powerful, auto-completing query builder. Whether you're building a simple blog or a complex SaaS, Prisma streamlines database access and lets you focus on your application logic.