Using Prisma ORM with PostgreSQL: A Comprehensive Guide

Using Prisma ORM with PostgreSQL: A Comprehensive Guide

Prisma is a modern, type-safe, and highly productive Object-Relational Mapping (ORM) tool that simplifies database access and management for developers. It supports multiple databases, including PostgreSQL, one of the most popular relational databases. In this article, we’ll explore how to use Prisma ORM with PostgreSQL, covering setup, schema definition, CRUD operations, and advanced features.

Why Use Prisma with PostgreSQL?

Prisma offers several advantages when working with PostgreSQL:

  1. Type Safety: Prisma generates TypeScript types based on your database schema, ensuring type safety and reducing runtime errors.

  2. Developer Productivity: Prisma’s intuitive API and auto-generated queries simplify database interactions.

  3. Migrations: Prisma provides a robust migration system to manage schema changes over time.

  4. Database Agnostic: While this article focuses on PostgreSQL, Prisma supports other databases like MySQL, SQLite, and SQL Server.

  5. Real-Time Data: Prisma integrates well with tools like GraphQL and REST APIs, enabling real-time data fetching.

Setting Up Prisma with PostgreSQL

Step 1: Install Prisma

First, ensure you have Node.js installed. Then, create a new Node.js project and install Prisma as a dependency:

mkdir server
cd server
npm init -y
npm install prisma typescript ts-node @types/node --save-dev

Step 2: Initialize Prisma

Initialize Prisma in your project:

npx prisma init

This command creates a prisma directory with a schema.prisma file and a .env file for environment variables.

Step 3: Configure PostgreSQL Connection

Open the .env file and update the DATABASE_URL to point to your PostgreSQL database:

DATABASE_URL="postgresql://USER:PASSWORD@HOST:PORT/DATABASE_NAME"

Replace USER, PASSWORD, HOST, PORT, and DATABASE_NAME with your PostgreSQL credentials.


Defining Your Database Schema

The schema.prisma file is where you define your database schema using Prisma’s declarative syntax. Here’s an example schema for a blog application:

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

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

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

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

In this schema:

  • User and Post are models representing tables in the database.

  • Relationships are defined using the @relation directive.

  • Fields are annotated with attributes like @id, @unique, and @default.


Applying the Schema to PostgreSQL

To create the tables in your PostgreSQL database, run the following command:

npx prisma migrate dev --name init

This command:

  1. Generates a migration file based on your schema.

  2. Applies the migration to the database.

  3. Creates the User and Post tables.


Performing CRUD Operations with Prisma Client

Prisma Client is an auto-generated query builder that allows you to interact with your database. Let’s explore basic CRUD operations.

1. Create a New User and Post

import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();

async function main() {
  const user = await prisma.user.create({
    data: {
      email: 'business.pulkitgarg@gmail.com',
      name: 'Pulkit Garg',
      posts: {
        create: {
          title: 'Using Prisma ORM with PostgreSQL...',
          content: 'Prisma is a modern...',
        },
      },
    },
  });
  console.log('Created user with post:', user);
}

main()
  .catch((e) => console.error(e))
  .finally(async () => await prisma.$disconnect());

2. Fetch All Users with Their Posts

async function main() {
  const users = await prisma.user.findMany({
    include: { posts: true },
  });
  console.log('All users with posts:', JSON.stringify(users, null, 2));
}

3. Update a Post

async function main() {
  const updatedPost = await prisma.post.update({
    where: { id: 1 },
    data: { published: true },
  });
  console.log('Updated post:', updatedPost);
}

4. Delete a User

async function main() {
  const deletedUser = await prisma.user.delete({
    where: { id: 1 },
  });
  console.log('Deleted user:', deletedUser);
}

Advanced Features

1. Transactions

Prisma supports transactions for executing multiple operations atomically:

const createUserAndPost = async () => {
  await prisma.$transaction([
    prisma.user.create({ data: { email: 'business.pulkitgarg@gmail.com', name: 'Pulkit Garg' } }),
    prisma.post.create({ data: { title: 'My Second Post Title', authorId: 1 } }),
  ]);
};

2. Raw SQL Queries

For complex queries, you can use raw SQL:

const users = await prisma.$queryRaw`SELECT * FROM User WHERE name = 'Pulkit Garg'`;

3. Middleware

Prisma allows you to add middleware to intercept queries:

prisma.$use(async (params, next) => {
  console.log('Query:', params);
  return next(params);
});

4. Data Validation

Prisma integrates with libraries like Zod for additional data validation.


Conclusion

Prisma ORM is a powerful tool for working with PostgreSQL, offering type safety, productivity, and flexibility. By following this guide, you can set up Prisma, define your schema, perform CRUD operations, and leverage advanced features like transactions and raw SQL queries. Whether you’re building a small application or a large-scale system, Prisma simplifies database management and helps you focus on building great software.

For more information, check out the Prisma documentation. Happy coding!