Home / Articles

Getting started with Prisma on Node.js and PostgreSQL

Install Prisma in a Node.js project, define a PostgreSQL schema, run the first migration, generate the TypeScript client and make first reads and writes.

Publisher: REACTAPP.TOP5 min read1096 words

This article is published in English.

Sources: Getting Started with Prisma: Quick and Easy Node.js Setup

Introduction

Prisma is a practical ORM choice for Node and TypeScript services that need typed database access. The walkthrough below is a fast path from a basic Node project to migrations, a generated client, and a few read/write queries against PostgreSQL. Prisma sits between application TypeScript and PostgreSQL: the schema file is the contract, migrations move the database, and the generated client gives typed methods for each model. Skipping any of those three usually shows up later as either runtime SQL errors or TypeScript that cannot see the fields the database already has.

Have a Node project ready first—and TypeScript if that is the project language.

Let’s set up Prisma

Step 1: Install Prisma and Prisma Client

Install both packages in the project directory:

npm install prisma @prisma/client
  • prisma is the CLI for schema work, migrations, and client generation.
  • @prisma/client is the library application code imports to query the database.

Step 2: Initialize Prisma

Initialize with PostgreSQL and a custom client output path:

npx prisma init --datasource-provider postgresql --output ../generated/prisma

What this command does:

  • Creates a prisma directory with schema.prisma for connection settings and models.
  • Sets the datasource to PostgreSQL.
  • Creates a root .env file for secrets such as the database URL.
  • --output ../generated/prisma redirects generated client output away from the default location.

Note: prisma init defaults to PostgreSQL. For another engine—or when --datasource-provider was omitted—edit the datasource block in schema.prisma.

PostgreSQL example:

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

Put the connection string in .env:

DATABASE_URL="postgresql://USER:PASSWORD@HOST:PORT/DATABASE?schema=SCHEMA"

Replace USER, PASSWORD, HOST, PORT, DATABASE, and SCHEMA with real values before modeling or migrating.

Step 3: Add Prisma schema and run migrations

Use Prisma Migrate to create tables. Add a starter model to prisma/schema.prisma:

model Post {
  id        Int      @id @default(autoincrement())
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
  title     String   @db.VarChar(255)
  content   String?
  published Boolean  @default(false)
  author    User     @relation(fields: [authorId], references: [id])
  authorId  Int
}

model Profile {
  id     Int     @id @default(autoincrement())
  bio    String?
  user   User    @relation(fields: [userId], references: [id])
  userId Int     @unique
}

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

For deeper modeling guidance, see the official data model docs. For this setup path, the sample model is enough to run a first migration.

Create and apply the migration:

npx prisma migrate dev --name init

This will:

  • generate SQL from the schema
  • apply it to the database
  • refresh the client so models are usable in code

--name init is only a label. Useful names later might be add-post-model or add-user-profile.

Step 4: Generate Prisma Client

Generate the client after install:

npx prisma generate

Generation reads schema.prisma and writes TypeScript types and query methods under generated/prisma (or the configured output). Import the constructor and create an instance:

import { PrismaClient } from './generated/prisma'

const prisma = new PrismaClient()

That instance can read and write through models such as User, Post, and Profile.

Good to know:

When prisma generate runs, it emits code tailored to the current schema. After schema edits, regenerate. Keeping the database aligned usually means prisma migrate dev or prisma db push; those commands also regenerate the client under the hood.

Step 5: Querying the database using TypeScript and PostgreSQL

With a generated client, start with a small script.

1. Create a new file named index.ts:

import { PrismaClient } from './generated/prisma'

const prisma = new PrismaClient()

async function main() {
  // ... you will write your Prisma Client queries here
}

main()
  .then(async () => {
    await prisma.$disconnect()
  })
  .catch(async (e) => {
    console.error(e)
    await prisma.$disconnect()
    process.exit(1)
  })

2. Reading data — list users inside main:

async function main() {
  // ... you will write your Prisma Client queries here
  const allUsers = await prisma.user.findMany()
  console.log(allUsers)
}

3. Writing data — create related records:

async function main() {
  await prisma.user.create({
    data: {
      name: 'Alice',
      email: 'alice@prisma.io',
      posts: {
        create: { title: 'Hello World' },
      },
      profile: {
        create: { bio: 'I like turtles' },
      },
    },
  })

  const allUsers = await prisma.user.findMany({
    include: {
      posts: true,
      profile: true,
    },
  })
  console.dir(allUsers, { depth: null })
}
  • prisma.user.create inserts a user with related post and profile data.
  • findMany with include loads related posts and profile.
  • console.dir with full depth prints nested objects clearly.

Treat generation as part of the edit loop, not a one-time bootstrap. Schema changes without regeneration leave application code talking to yesterday’s types. Migrations without a matching client leave runtime and compile-time views of the database out of sync. A simple habit—migrate (or push), then confirm the client output updated—prevents a large class of “why doesn’t TypeScript know about this field?” moments.

For local development, keep .env out of version control and document the expected DATABASE_URL shape in the README. For CI, inject the same variable from secrets and run migrate deploy (or an equivalent gated step) before integration tests that touch the database. The setup above is enough to learn the client; production hardening adds connection pooling, a shared client singleton, and explicit disconnect on process shutdown.

What’s next?

That covers a TypeScript Node setup with Prisma: install, initialize, migrate, generate, and run basic reads and writes. The next depth layer is data modeling—relationships, constraints, and advanced schema features that keep the database layer expressive as the app grows.