Skip to content

Part 3.1 — Project Setup & Prisma Schema

Site Console Site Console
3 min read Updated Jan 19, 2026 Backend Development 0 comments

If your frontend is the face of your application, your backend is the backbone.
Part 3 begins your journey into a real, production-quality backend using NestJS + Prisma + PostgreSQL, the stack we'll reuse consistently for the rest of the series.

This post lays the foundation: project setup, environment config, Prisma installation, PostgreSQL connection, schema modeling, migrations, and initial folder structure.


1. Why NestJS + Prisma + PostgreSQL?

This stack gives you:

  • NestJS: structured, modular TypeScript backend with decorators, DI, pipes, filters, interceptors

  • Prisma: type-safe ORM that lets you query PostgreSQL without raw SQL footguns

  • PostgreSQL: powerful, ACID-compliant RDBMS ideal for production workloads

Everything we build moving forward uses this stack — no alternatives.


2. Create the Backend Project

Inside your monorepo (or root workspace):

pnpm dlx @nestjs/cli new backend

Choose TypeScript.
Move into the project:

cd backend

Install dependencies:

pnpm install

Your project now has:

backend/
  src/
  test/
  package.json
  tsconfig.json

This is your home for all backend code.


3. Install Prisma & Initialize It

From inside backend/:

pnpm add -D prisma
pnpm add @prisma/client

Initialize:

pnpm prisma init

This creates:

prisma/
  schema.prisma
.env

We’ll configure PostgreSQL next.


4. Configure PostgreSQL Connection

Open your .env:

DATABASE_URL="postgresql://postgres:password@localhost:5432/fullstack_db"

If you’re using Docker for PostgreSQL (recommended):

# docker-compose.yml
services:
  db:
    image: postgres:15
    ports:
      - "5432:5432"
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: password
      POSTGRES_DB: fullstack_db

Start:

docker compose up -d

Your backend now has a clean PostgreSQL instance.


5. Define the Initial Prisma Schema

Open prisma/schema.prisma:

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

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

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

model Post {
  id        Int     @id @default(autoincrement())
  title     String
  body      String
  createdAt DateTime @default(now())
  authorId  Int
  author    User     @relation(fields: [authorId], references: [id])
}

Why this schema?

  • Shows a 1-to-many relation (User → Posts)

  • Matches typical CRUD operations

  • Will integrate easily with your earlier React CRUD UI


6. Generate the Client & Run the Migration

Generate Prisma Client:

pnpm prisma generate

Run the first migration:

pnpm prisma migrate dev --name init

Prisma will create:

  • the tables

  • indexes

  • foreign keys

  • relation integrity

Your PostgreSQL database now has a working schema.


7. Add PrismaService to NestJS

Create a Prisma module:

pnpm nest g module prisma
pnpm nest g service prisma

Edit prisma.service.ts:

import { Injectable, OnModuleInit, OnModuleDestroy } from '@nestjs/common';
import { PrismaClient } from '@prisma/client';

@Injectable()
export class PrismaService
  extends PrismaClient
  implements OnModuleInit, OnModuleDestroy
{
  async onModuleInit() {
    await this.$connect();
  }
  async onModuleDestroy() {
    await this.$disconnect();
  }
}

Add it to the module:

import { Module } from '@nestjs/common';
import { PrismaService } from './prisma.service';

@Module({
  providers: [PrismaService],
  exports: [PrismaService],
})
export class PrismaModule {}

This gives your entire backend access to PrismaService.


8. Folder Structure Going Forward

We’ll follow a clear module-based layout:

src/
  prisma/
  users/
    users.module.ts
    users.service.ts
    users.controller.ts
    dto/
  posts/
    posts.module.ts
    posts.service.ts
    posts.controller.ts
    dto/
  common/
    exceptions/
    filters/
    pipes/
    interceptors/

This structure supports scalability without the chaos of sprawling files.


9. Quick Smoke Test Query

Edit app.service.ts temporarily:

constructor(private prisma: PrismaService) {}

getHello() {
  return this.prisma.user.findMany();
}

Run:

pnpm start:dev

Hit:
http://localhost:3000/

You should get [] — an empty users array — confirming your stack works:

React → REST client → NestJS → Prisma → PostgreSQL (coming soon in Part 5)


10. What’s Next?

In Part 3.2, you’ll create:

  • DTO validation

  • REST controllers

  • REST routes

  • Services with Prisma queries

  • Production-ready request validation

This is where your backend truly becomes usable by your frontend.

Related

Leave a comment

Sign in to leave a comment.

Comments