The goal of this lesson is get comfortable with the Prisma Client API and explore some of the available database queries you can send with it. You'll learn about CRUD queries, relation queries (like nested writes), filtering and pagination. Along the way, you will run another migration to introduce a second model with a *relation* to the User
model that you created before.
You can continue working in the same prisma-workshop
project that you set up in lesson 1. The script.ts
file contains a main
function that is invoked each time the script is executed.
-
Type yourself, don't copy and paste
To learn and really understand what you are doing for each task, be sure to not copy and paste the solution but type out the solution yourself (even if you have to look it up).
-
Autocompletion
Prisma Client provides a number of queries that you can send to your database using its API. You can learn about these queries in the documentation or explore the API right in your editor using autocompletion.
To invoke the autocompletion, you can open
src/index.ts
and type following inside of themain
function (you can delete the comment// ... your Prisma Client queries will go here
that's currently there):import { PrismaClient } from '@prisma/client' const prisma = new PrismaClient() async function main() { const result = await **prisma.** // autocompletion will show up if you type this } main() .catch((e) => console.error(e)) .finally(async () => await prisma.$disconnect())
-
Expand for a screenshot of the autocompletion
Once you typed the line
const result = await prisma.
****into your editor, a little popup will be shown that lets you select the options for composing a query (e.g. selecting a model you want to query or using another top-level function like$queryRaw
or$connect
). Autocompletion is available for the entire query, including any arguments that you might want to provide! -
-
Prisma Studio
Prisma Studio is a GUI for your database that you can use to view and edit the data inside. You can start Prisma Studio by running the following command:
npx prisma studio
At the end of each task, you can run the script using the following command:
npm run dev
To warm yourself up a bit, go and write a query to return all User
records from the database. Print the result to the console using console.log
.
-
Solution
import { PrismaClient } from '@prisma/client' const prisma = new PrismaClient() async function main() { const result = await prisma.user.findMany() ****console.log(result) } main() .catch((e) => console.error(e)) .finally(async () => await prisma.$disconnect())
In this task you'll create another User
record. In your Prisma Client query, provide only a value for email
but not for name
:
email
:"[email protected]"
Can you find the query that lets you do that?
-
Solution
import { PrismaClient } from '@prisma/client' const prisma = new PrismaClient() async function main() { const result = await prisma.user.create({ data: { email: "[email protected]" } }) console.log(result) } main() .catch((e) => console.error(e)) .finally(async () => await prisma.$disconnect())
In this task, you will update the User
record you just created and add a value for its name
field:
name
:"Alice"
How can you update an existing database record with Prisma Client?
-
Solution
import { PrismaClient } from '@prisma/client' const prisma = new PrismaClient() async function main() { const result = await prisma.user.update({ where: { email: "[email protected]" }, data: { name: "Alice" } }) console.log(result) } main() .catch((e) => console.error(e)) .finally(async () => await prisma.$disconnect())
To explore more interesting Prisma Client queries, let's expand your Prisma schema with another model and configure a relation between the existing and the new one.
The new Post
model should be shaped as follows:
-
id
: an auto-incrementing integer to uniquely identify each post in the database -
title
: the title of a post; this field should be required in the database -
content
: the content/body of the post; this field should be optional in the database -
published
: indicates whether a post is published or not; this field should be required in the database; by default any post that is created should not be published -
author
andauthorId
: configures a relation from a post to a user who should be considered the author of the post; the relation should be optional so that a post doesn't necessarily need an author in the database; note that all relations in Prisma are bi-directional, meaning you'll need to add the second side of the relation on the already existingUser
model as well -
Solution
model User { id Int @id @default(autoincrement()) name String? email String @unique posts Post[] } model Post { id Int @id @default(autoincrement()) title String content String? published Boolean @default(false) author User? @relation(fields: [authorId], references: [id]) authorId Int? }
Once you have adjusted the Prisma schema and your two models are in place, run a migration to apply the changes against your database:
npx prisma migrate dev --name add-post
In this task, you'll create a first Post
record with the title "Hello World"
.
-
Solution
import { PrismaClient } from '@prisma/client' const prisma = new PrismaClient() async function main() { const result = await prisma.post.create({ data: { title: "Hello World" } }) console.log(result) } main() .catch((e) => console.error(e)) .finally(async () => await prisma.$disconnect())
You now have several User
records and exactly one Post
record in the database, these can be connected via the authorId
foreign key column in the database.
When using Prisma Client, you don't need to manually set foreign keys but you can configure relations using Prisma Client's type-safe API. Can you figure out how you can update the Post
record and connect it to an existing User
record via the email
field?
Use the editor's autocompletion to find out about the query or read the documentation.
-
Solution
import { PrismaClient } from "@prisma/client"; const prisma = new PrismaClient(); async function main() { const result = await prisma.post.update({ where: { id: 1 }, data: { author: { connect: { email: "[email protected]" }, }, }, }); console.log(result); } main() .catch((e) => console.error(e)) .finally(async () => await prisma.$disconnect());
In task 1, you learned how to fetch a list of records from the database. In this task, you need to retrieve a single User
record with a Prisma Client query by a unique value.
-
Solution
import { PrismaClient } from "@prisma/client"; const prisma = new PrismaClient(); async function main() { const result = await prisma.user.findUnique({ where: { email: "[email protected]" } }) console.log(result) } main() .catch((e) => console.error(e)) .finally(async () => await prisma.$disconnect());
Note that you can use any unique field of a Prisma model to identify a record via the
where
argument, so in this case you could identify aUser
record by itsid
as well.
For this task, you can reuse the same findMany
query for users that you used in task 1. However, this time your goal is to only select a subset of the fields of the User
model, specifically all returned objects should only contain the id
and name
**but not the email
field.
-
Solution
import { PrismaClient } from "@prisma/client"; const prisma = new PrismaClient(); async function main() { const result = await prisma.user.findMany({ select: { id: true, name: true } }) console.log(result) } main() .catch((e) => console.error(e)) .finally(async () => await prisma.$disconnect());
You'll now start exploring more relation queries of Prisma Client! Let's start with a nested read where you include a relation, concretely: Take your query from task 7 and include the relation to the Post
table in the result.
-
Solution
import { PrismaClient } from "@prisma/client"; const prisma = new PrismaClient(); async function main() { const result = await prisma.user.findUnique({ where: { email: "[email protected]" }, include: { posts: true }, }); console.dir(result, { depth: null })} main() .catch((e) => console.error(e)) .finally(async () => await prisma.$disconnect());
Notice that the the
result
of your query is fully typed! The type for it is generated on the fly by Prisma Client, here's what it looks like:const result: (User & { posts: Post[]; }) | null // ... where the `Post` and `User` types look as follows: type Post = { id: number title: string content: string | null published: boolean authorId: number | null } type User = { id: number name: string | null email: string }
In this task, you'll create a new User
along with a new Post
record in a single Prisma Client (nested write) query. You can again use the autocompletion to figure out the right query or read the documentation here.
-
Solution
import { PrismaClient } from "@prisma/client"; const prisma = new PrismaClient(); async function main() { const result = await prisma.user.create({ data: { name: "Nikolas", email: "[email protected]", posts: { create: { title: "A practical introduction to Prisma" }, }, }, }); console.log(result); } main() .catch((e) => console.error(e)) .finally(async () => await prisma.$disconnect());
For this task, you can again reuse the same findMany
query for users that you used in task 1. Only that this time, you don't want to return all User
records, but only those that have a name
which starts with the letter "A"
. Can you find the right operator that lets you express this condition?
-
Solution
import { PrismaClient } from "@prisma/client"; const prisma = new PrismaClient(); async function main() { const result = await prisma.user.findMany({ where: { name: { startsWith: "A", }, }, }); console.log(result); } main() .catch((e) => console.error(e)) .finally(async () => await prisma.$disconnect());
Prisma Client provides several ways to paginate over a list of objects. Use the findMany
query from before to return only the third and fourth User
records in the database.
-
Solution
import { PrismaClient } from "@prisma/client"; const prisma = new PrismaClient(); async function main() { const result = await prisma.user.findMany({ skip: 2, take: 2, }); console.log(result); } main() .catch((e) => console.error(e)) .finally(async () => await prisma.$disconnect());
With theses tasks, you only scratched the surface of what's possible with the Prisma Client API. Feel free to explore more queries and try out some of the ordering, upsert, plain SQL or other features.