Full-stack Web Technologies

CHAPTER 4
Prisma Client

Prisma Client is a generated library specific for your database schema. With this client library, you can do all possible operations to your database with the comfort of autocompletion by Typescript.

Generation

To generate the Prisma Schema you must invoke the prisma CLI with the generate subcommand:

bunx prisma generate

This will generate the source code for the @prisma/client package, which will be exactly adapted to the schema you provided (Typescript types and all).

Usage

Import PrismaClient and instantiate a global object:

import { PrismaClient } from "@prisma/client";
export default new PrismaClient();

or, equivalently

import { PrismaClient } from "@prisma/client";
export const db = new PrismaClient();

Every entity defined in the schema appears as a field of the client, so if a model called User exists, the prisma.user field will represent the table/collection of entities.

CRUD Operations

Every field of prisma representing a collection has the following methods:

findFirst

First entity satisfying a condition

findFirstOrThrow

Throwing version of findFirst

findUnique

Single entity (only if @id or @unique)

findUniqueOrThrow

Throwing version of findUnique

findMany

All entities satisfying a condition

create

Create an entity

createMany

Create many entities

update

Update an entity

updateMany

Update many entites satisfying a condition

upsert

Update or insert if it doesn't exist

delete

Delete an entity

deleteMany

Delete many entities satisfying a condition

Schema for examples

The following schema is assumed in all the examples given afterwards:

model User {
  id           Int              @id @default(autoincrement())
  name         String?
  email        String           @unique
  profileViews Int              @default(0)
  role         Role             @default(USER)
  coinflips    Boolean[]
  posts        Post[]
  city         String
  country      String
  profile      ExtendedProfile?
  pets         Json
}

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

model Post {
  id        Int     @id @default(autoincrement())
  title     String
  published Boolean @default(true)
  author    User    @relation(fields: [authorId], references: [id])
  authorId  Int
  comments  Json
  views     Int     @default(0)
  likes     Int     @default(0)
}

enum Role {
  USER
  ADMIN
}
Create

Create a new user:

const user = await prisma.user.create({
  data: { email: "alice@prisma.io" },
});

Create many users:

const user = await prisma.user.createMany({
  data: [{ email: "alice@prisma.io" }, { email: "bob@prisma.io" }],
});
Update

Update a user:

const user = await prisma.user.update({
  where: { id: 1 },
  data: { email: "bob@prisma.io" },
});
Upsert

Upsert an admin user with email alice@prisma.io or create if it doesn't exist:

const user = await prisma.user.upsert({
  where: { email: "alice@prisma.io" },
  update: { role: "ADMIN" },
  create: {
    role: "ADMIN",
    email: "alice@prisma.io",
  },
});

Queries

Find the user with email of alice@prisma.io:

await prisma.user.findUnique({
  where: { email: { equals: "alice@prisma.io" } },
});

Find the user with id 42 (equals not necessary):

await prisma.user.findUnique({ where: { id: 42 } });

Find all users with name Alice:

await prisma.user.findMany({ where: { name: "Alice" } });

Find a post with title starting with "A test":

await prisma.post.create({ data: { title: "A test 1" } });
await prisma.post.create({ data: { title: "A test 2" } });
await prisma.post.findFirst({
  where: { title: { startsWith: "A test" } },
  orderBy: { title: "asc" },
  take: -1, // Start from the end and take 1
});
Options object

CRUD methods all receive an options method of a certain type (depending on the operation).

whereThe filter, a condition that entities must satisfy
selectWhich fields that should be included in the result
takeHow many entities should be returned
skipHow many entities to skip in the result
includeWhich relationships should be eagerly loaded
orderByThe order of the result
distinctFilter out duplicate rows by field
Filter operators
equals{ name: { equals: 'Jose' } }
{ name: 'Jose' }
not{ name: { not: 'Jose' } }
in{ name: { in: ['Groucho', 'Chicco', 'Harpo'] } }
notIn{ id: { notIn: [22, 91, 14, 2, 5] } }
lt{ likes: { lt: 9 } }
lte{ likes: { lte: 9 } }
gt{ likes: { gt: 9 } }
gte{ likes: { gte: 9 } }
contains{ text: { contains: 'fullstack' } }
search{ title: { search: 'frontend & backend' } }
startsWith{ title: { startsWith: 'The' } }
endsWith{ email: { endsWith: '@proton.me' } }
AND{ AND: [{ name: 'Jose' }, { age: 35 }] }
OR{ OR: [{ name: 'Jose' }, { name: 'Pau' }] }
NOT{ NOT: { name: 'Jose' } }

Relations

The include option specifies which related entities should be populated:

const users = await prisma.user.findMany({
  include: {
    posts: true,
    profile: true,
  },
});

Filters also can affect relations:

someSome related entities satisfy condition
everyEvery related entity satisfies condition
noneNo related entities satisfy condition
isPopulate with related entities satisfying filter
isNotPopulate with related entities NOT satisfying filter

Aggregation

Some methods of a table/collection can compute aggregate results, count or group by certain fields:

count

Count entities satisfying a condition

aggregate

Count entities satisfying a condition

groupBy

Count entities satisfying a condition

Count

The count method can compute counts of entites satisfying a certain condition:

// Count all users
const nusers = await prisma.user.count();

// Count users with a post published
const onePub = await prisma.user.count({
  where: {
    post: {
      some: { published: true },
    },
  },
});
Aggregate

The aggregate method can compute many aggregation statistics at once:

const minMaxAge = await prisma.user.aggregate({
  _count: {
    _all: true,
  },
  _max: {
    profileViews: true,
  },
  _min: {
    profileViews: true,
  },
});
GroupBy

The groupBy method can group entities by some fields, and then do aggregations on them:

const groupUsers = await prisma.user.groupBy({
  by: ["country", "city"],
  _count: { _all: true, city: true },
  _sum: { profileViews: true },
  orderBy: { country: "desc" },
  having: { profileViews: { _avg: { gt: 200 } } },
});