Skip to content

Prisma

prismanextjsotelpostgresql

Schema Best Practice

@auth/prisma-adapter requires some models for PostgreSQL but some models like Session, VerificationToken are unused when using JWT strategy of next-auth. This template pre-deletes them.

prisma
// https://pris.ly/d/prisma-schema

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

generator client {
  provider        = "prisma-client-js"
  previewFeatures = ["fullTextSearchPostgres"]
}

generator erd {
  provider                  = "prisma-erd-generator"
  theme                     = "forest"
  output                    = "ERD.md"
  includeRelationFromFields = true
}

// https://authjs.dev/getting-started/adapters/prisma#schema
model Account {
  id                String   @id @default(cuid())
  userId            String   @map("user_id")
  type              String
  provider          String
  providerAccountId String   @map("provider_account_id")
  refresh_token     String?  @db.Text
  access_token      String?  @db.Text
  expires_at        Int?
  token_type        String?
  scope             String?
  id_token          String?  @db.Text
  user              User     @relation(fields: [userId], references: [id], onDelete: Cascade)
  createdAt         DateTime @default(now()) @map("created_at")
  updatedAt         DateTime @updatedAt @map("updated_at")

  @@unique([provider, providerAccountId])
  @@map("accounts")
}

model User {
  id            String    @id @default(cuid())
  name          String?
  email         String?   @unique
  emailVerified DateTime? @map("email_verified")
  image         String?
  accounts      Account[]
  createdAt     DateTime  @default(now()) @map("created_at")
  updatedAt     DateTime  @updatedAt @map("updated_at")
  // https://authjs.dev/guides/basics/role-based-access-control
  role          String?
  // example model
  items         Item[]

  @@map("users")
}

// example model
model Item {
  id        String   @id @default(cuid())
  content   String
  user      User     @relation(fields: [userId], references: [id], onDelete: Cascade)
  userId    String   @map("user_id")
  createdAt DateTime @default(now()) @map("created_at")
  updatedAt DateTime @updatedAt @map("updated_at")

  @@map("items")
}

ER diagram

prisma-erd-generator can generate ERD from the schema. The current template ERD is here.

See Full Code
md
```mermaid
erDiagram

  "accounts" {
    String id "🗝️"
    String user_id
    String type
    String provider
    String provider_account_id
    String refresh_token "❓"
    String access_token "❓"
    Int expires_at "❓"
    String token_type "❓"
    String scope "❓"
    String id_token "❓"
    DateTime created_at
    DateTime updated_at
    }


  "users" {
    String id "🗝️"
    String name "❓"
    String email "❓"
    DateTime email_verified "❓"
    String image "❓"
    DateTime created_at
    DateTime updated_at
    String role "❓"
    }


  "items" {
    String id "🗝️"
    String content
    String user_id
    DateTime created_at
    DateTime updated_at
    }

    "accounts" o|--|| "users" : "user"
    "users" o{--}o "accounts" : "accounts"
    "users" o{--}o "items" : "items"
    "items" o|--|| "users" : "user"
```

Making single client instance in Development Best Practice

In development, prisma requires avoiding multiple Prisma Client instances but Hot Module Replacement creates them. So this template prepares code to resolve this issue.

ts
// https://www.prisma.io/docs/orm/more/help-and-troubleshooting/help-articles/nextjs-prisma-client-dev-practices

import { PrismaClient } from "@prisma/client";
import { createDBUrl } from "../_utils/db";

function prismaClientSingleton() {
  return new PrismaClient({
    datasources: {
      db: {
        url: createDBUrl({}),
      },
    },
  });
}

// biome-ignore lint: Do not shadow the global "globalThis" property.
declare const globalThis: {
  prismaGlobal: ReturnType<typeof prismaClientSingleton>;
} & typeof global;

export const prisma = globalThis.prismaGlobal ?? prismaClientSingleton();

if (process.env.NODE_ENV !== "production") {
  globalThis.prismaGlobal = prisma;
}

> `datasources.db.url` is always set for parallel execution by testing

Observability Best Practice

Prisma provides OpenTelemetry tracing feature. This template uses @prisma/instrumentation and you can view Prisma Query, Engine, and Database Query on Jeager via otel-collector. Learn more here

query metric