# Series: It's Prisma Time (16 Parts) β€” Luca Del Puppo > Complete 16-part technical series "It's Prisma Time" by Luca Del Puppo (https://delpuppo.net/blog/series/it-s-prisma-time/). Ordered chronologically from Part 1 to Part 16. - **Canonical Series URL**: https://delpuppo.net/blog/series/it-s-prisma-time/ - **Hashnode Series Mirror**: https://blog.delpuppo.net/series/it-s-prisma-time - **Blog Index LLMs.txt**: https://delpuppo.net/blog/llms.txt ## Series Curriculum 1. [Part 1: It's Prisma Time - Introduction](https://delpuppo.net/blog/it-s-prisma-time/its-prisma-time-introduction/) (Dec 14, 2021) β€” Hi Folks πŸ‘‹ and welcome back, Today I want to start a new series about Prisma. But before starting I... ([Article LLMs.txt](https://delpuppo.net/blog/it-s-prisma-time/its-prisma-time-introduction/llms.txt)) 2. [Part 2: It's Prisma Time - Setup](https://delpuppo.net/blog/it-s-prisma-time/its-prisma-time-setup/) (Dec 16, 2021) β€” Hi Guys, welcome to the second article of this series. Today I'll show you how to setup a project... ([Article LLMs.txt](https://delpuppo.net/blog/it-s-prisma-time/its-prisma-time-setup/llms.txt)) 3. [Part 3: It's Prisma Time - Create Table](https://delpuppo.net/blog/it-s-prisma-time/its-prisma-time-create-table/) (Dec 21, 2021) β€” Hello Folks πŸ‘‹ and welcome back to this series, today it's time to create your first table with... ([Article LLMs.txt](https://delpuppo.net/blog/it-s-prisma-time/its-prisma-time-create-table/llms.txt)) 4. [Part 4: It's Prisma Time - Create Db](https://delpuppo.net/blog/it-s-prisma-time/its-prisma-time-create-db/) (Dec 23, 2021) β€” Hi Guys πŸ‘‹ welcome back. Finally, today it's time to create our database, so don't waste time, and get... ([Article LLMs.txt](https://delpuppo.net/blog/it-s-prisma-time/its-prisma-time-create-db/llms.txt)) 5. [Part 5: It's Prisma Time - Create Relations](https://delpuppo.net/blog/it-s-prisma-time/its-prisma-time-create-relations/) (Dec 28, 2021) β€” Hi Folks πŸ‘‹ today I'll show you how to create relations among your entities. So don't waste time and... ([Article LLMs.txt](https://delpuppo.net/blog/it-s-prisma-time/its-prisma-time-create-relations/llms.txt)) 6. [Part 6: It's Prisma Time - Insert](https://delpuppo.net/blog/it-s-prisma-time/its-prisma-time-insert/) (Dec 30, 2021) β€” Welcome Guys, Today it's time to add some records to the database 😁 so don't waste time and get your... ([Article LLMs.txt](https://delpuppo.net/blog/it-s-prisma-time/its-prisma-time-insert/llms.txt)) 7. [Part 7: It's Prisma Time - Delete](https://delpuppo.net/blog/it-s-prisma-time/its-prisma-time-delete/) (Jan 5, 2022) β€” Hi Guys πŸ‘‹ today we are going to see the delete operation. Before moving to the delete operation, in... ([Article LLMs.txt](https://delpuppo.net/blog/it-s-prisma-time/its-prisma-time-delete/llms.txt)) 8. [Part 8: It's Prisma Time - Update](https://delpuppo.net/blog/it-s-prisma-time/its-prisma-time-update/) (Jan 7, 2022) β€” Hi Folks πŸ‘‹ Today it's time to learn something about the update operation, so don't waste time and... ([Article LLMs.txt](https://delpuppo.net/blog/it-s-prisma-time/its-prisma-time-update/llms.txt)) 9. [Part 9: It's Prisma Time - Select](https://delpuppo.net/blog/it-s-prisma-time/its-prisma-time-select/) (Jan 11, 2022) β€” Welcome back Guys, Today it's time to get our record using the select command. Let's start with a... ([Article LLMs.txt](https://delpuppo.net/blog/it-s-prisma-time/its-prisma-time-select/llms.txt)) 10. [Part 10: It's Prisma Time - Pagination](https://delpuppo.net/blog/it-s-prisma-time/its-prisma-time-pagination/) (Jan 13, 2022) β€” Hi Guys πŸ‘‹ Today we are going to see how to paginate our results. So don't waste time and let's... ([Article LLMs.txt](https://delpuppo.net/blog/it-s-prisma-time/its-prisma-time-pagination/llms.txt)) 11. [Part 11: It's Prisma Time - Aggregate and GroupBy](https://delpuppo.net/blog/it-s-prisma-time/its-prisma-time-aggregate-and-groupby/) (Jan 18, 2022) β€” Welcome back Folks πŸ‘‹ Today we are seeing how to aggregate and group data. Let's start from the... ([Article LLMs.txt](https://delpuppo.net/blog/it-s-prisma-time/its-prisma-time-aggregate-and-groupby/llms.txt)) 12. [Part 12: It's Prisma Time - Transactions](https://delpuppo.net/blog/it-s-prisma-time/its-prisma-time-transactions/) (Jan 20, 2022) β€” Hello Guys πŸ‘‹ Today it's time to speak about transactions. Transactions are a pillar of the database... ([Article LLMs.txt](https://delpuppo.net/blog/it-s-prisma-time/its-prisma-time-transactions/llms.txt)) 13. [Part 13: It's Prisma Time - Logging](https://delpuppo.net/blog/it-s-prisma-time/its-prisma-time-logging/) (Jan 26, 2022) β€” Hi Guys πŸ‘‹ Today we are going to see how to enable logging in Prisma, so don't waste time and let's... ([Article LLMs.txt](https://delpuppo.net/blog/it-s-prisma-time/its-prisma-time-logging/llms.txt)) 14. [Part 14: It's Prisma Time - Execute your own queries](https://delpuppo.net/blog/it-s-prisma-time/its-prisma-time-execute-your-own-queries/) (Jan 28, 2022) β€” Hello Folks πŸ‘‹ Today it's time to see how we can execute our own queries. In prisma we can run two... ([Article LLMs.txt](https://delpuppo.net/blog/it-s-prisma-time/its-prisma-time-execute-your-own-queries/llms.txt)) 15. [Part 15: It's Prisma Time - Migrations](https://delpuppo.net/blog/it-s-prisma-time/its-prisma-time-migrations/) (Feb 2, 2022) β€” Hello Guys πŸ‘‹, Today it's time to speak about migrations. Migrations in Prisma are managed by Prisma... ([Article LLMs.txt](https://delpuppo.net/blog/it-s-prisma-time/its-prisma-time-migrations/llms.txt)) 16. [Part 16: It's Prisma Time - Seeding](https://delpuppo.net/blog/it-s-prisma-time/its-prisma-time-seeding/) (Feb 4, 2022) β€” Hello Folks πŸ‘‹ Today we'll see how to handle seed with Prisma. Seeding allows you to re-create... ([Article LLMs.txt](https://delpuppo.net/blog/it-s-prisma-time/its-prisma-time-seeding/llms.txt)) --- ## Full Series Content ### Part 1 of 16: It's Prisma Time - Introduction - **URL**: https://delpuppo.net/blog/it-s-prisma-time/its-prisma-time-introduction/ - **Published**: Dec 14, 2021 - **Tags**: javascript, typescript, database, orm Hi Folks πŸ‘‹ and welcome back, Today I want to start a new series about [Prisma](https://www.prisma.io/). But before starting I want to explain the reason why of this series. In the last months I spent more time using [ORM](https://it.wikipedia.org/wiki/Object-relational_mapping) in the node eco-system. Most of the time I used [typeorm](https://typeorm.io/). In my opinion Typeorm is a great ORM but creates entities and managing them sometimes it's not so easy, especially if you love to use [typescript](https://www.typescriptlang.org/) without classes. But one day in my twitter's wall appeared this [tweet](https://twitter.com/matteocollina/status/1384900740754386946?s=20) of [@Matteo Collina](https://twitter.com/matteocollina) that spoke about [prisma](https://www.prisma.io/), so I decided to spend some of my free time to understand better this tool. ![The Collina's Tweet](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pvjt0ksn1c5vcgs159ap.png) As you can imagine, it left me good impressions so I decide to share them with you using this series. ## What's in this series? In this series, I'm going to show you how to set up a project with Prisma and how it can work in your codebase. I'm going to try to create shot articles with only one goal for each article, to permit you to understand better the goal and don't switch context during the reading. ## What's not in this series? In this series I am not going to show you how to create a repository to split the data access layer with the business logic layer or how to structure your project to have a clean code, that's not the goal of the series, and I know that already exist many books and articles that speak about it. _A little preamble: I don't love so much using ORM in my code, but sometimes they help the team to increase its velocity. Despite everything, it's important to check the queries generated by the ORM because sometimes they aren't optimized. In these cases, it's a good solution writing your query in order to improve the velocity of its execution._ Let's start with an introduction of **Prisma**. In this article I picked some infos about Prisma from its site. This helps you to have an idea of what it is and which are they goals. ## Introduction Prisma is an open source next-generation ORM. It consists of the following parts: - [Prisma Client](https://www.prisma.io/docs/concepts/components/prisma-client): Auto-generated and type-safe query builder for Node.js & TypeScript - [Prisma Migrate](https://www.prisma.io/docs/concepts/components/prisma-migrate): Migration system - [Prisma Studio](https://www.prisma.io/docs/concepts/components/prisma-studio): GUI to view and edit data in your database _It's important to remember that if you prefer using javascript without typescript, you can choose this way without any problem because Prisma guarantees both solutions._ ## Prisma Goals Prisma's main goal is to make application developers more productive when working with databases: - **Thinking in objects** instead of mapping relational data - **Queries not classes** to avoid complex model objects - **Single source of truth** for database and application models - **Healthy constraints** that prevent common pitfalls and antipatterns - **An abstraction that makes the right thing easy** ("pit of success") - **Type-safe database queries** that can be validated at compile time - **Less boilerplate** so developers can focus on the important parts of their app - **Auto-completion in code editors** instead of needing to look up documentation ## Prisma Connectors Prima allow us to connect to the major SQL and NOSQL services. - PostgreSQL - MySql - SQLite - MongoDB - Microsoft SQL Server I think that as an introduction of the tool and to give you an idea about it, it may be all. If you are curious and you want to go deep inside about Prisma, in its [documentation](https://www.prisma.io/docs/concepts/overview) you can find more info. I think for today it's all, but before ending this article, I wish to thank Prisma for the cover of this series, the original image is hosted in the [Prisma facebook account](https://www.facebook.com/prisma.io/) at this [in this reference](https://www.facebook.com/prisma.io/photos/a.1593544297636616/2378801429110895/?type=1&theater) So see you in the next article where we'll start to setup our project. Bye bye πŸ‘‹ --- ### Part 2 of 16: It's Prisma Time - Setup - **URL**: https://delpuppo.net/blog/it-s-prisma-time/its-prisma-time-setup/ - **Published**: Dec 16, 2021 - **Tags**: javascript, typescript, database, orm Hi Guys, welcome to the second article of this series. Today I'll show you how to setup a project with Prisma. This project in addition to prisma uses typescript, but let's start. The first thing that you need to do is to initialize a folder as a node project. To do this you need to run the following command in your terminal. (I used yarn but if you prefer npm or something else feel free to use it) ``` yarn init -y ``` I suppose that if you are here you know what you did running this command. We go on and add now to our workspace some dependencies using the next command. ``` yarn add -d typescript @types/node ts-node prisma ``` The first three dependencies are related to typescript and they are necessary to use typescript in our project, the last one is the [Prisma CLI](https://www.prisma.io/docs/reference/api-reference/command-reference). The Prisma CLI helps us to create, update or view our database, it is easy to use and it hasn't a lot of commands to remember. Now it's time to create our Prisma schema, for this series I decided to use SQLite as database service because it's the easiest to set up. Run the following command to generate the schema. ``` npx prisma init --datasource-provider sqlite ``` As you can see, this command created a file `schema.prisma` inside of the folder `prisma`. In this file, you can see two sections: client and db. The client section is used to identify the provider used to manage the connection with the source, whereas in the db section you can find the connection string to the database and the provider to identify the type of the source, in our case `SQLite`. The schema file is the single source of truth when we use Prisma, in this file, we add all the entities and all their relations. From this file, Prisma can generate our typescript types related to the entities and it can create our migration scripts to upgrade the schema of our source. As you can see, the schema file needs to access an environment variable(DATABASE_URL), so to do this, it was created a file `.env` in the root of the project where inside we can find this variable. The last thing I want to leave you today is related to the parameter `--datasource-provider` in the script. By default, if you don't pass this parameter the script uses PostgreSQL as database service, so if you need to use a different service you need to specify it using the `--datasource-provider` parameter. I think this is all for today. In the next article we are going to create our first table with Prisma and we are going to start playing with this tool. Ok guys, see you soon πŸ‘‹ _You can find the code of this article [in the GitHub repository](https://github.com/Puppo/it-s-prisma-time/tree/02-setup)_ --- ### Part 3 of 16: It's Prisma Time - Create Table - **URL**: https://delpuppo.net/blog/it-s-prisma-time/its-prisma-time-create-table/ - **Published**: Dec 21, 2021 - **Tags**: javascript, typescript, database, orm Hello Folks πŸ‘‹ and welcome back to this series, today it's time to create your first table with prisma, are you ready? Then let's go πŸ’ͺ Before starting, let me explain which type of tables we are going to create in this series. For this series we are going to create a simple db to manage a blog, ok.. not a big and strong blog but an easy blog to focus our learn on the main concepts of prisma. Since we’re talking about blogs, let's start from the post table obviously. Open the `prisma/schema.prisma` file and append the following snippet. ```ts model Post { id Int @id @default(autoincrement()) title String content String published Boolean @default(false) createAt DateTime @default(now()) updatedAt DateTime @updatedAt @@map("posts") } ``` Great! But what did you do adding this code? πŸ€” Writing this code you described the entity Post and you indicated to Prisma: - I want an entity called Post - This entity has 6 properties - The id property is of type Integer, it is the primary key of my entity (`@id`) and it must be auto-generated (`@default(autoincrement())`) - The title property is of type String - The content property is of type String - The published property is of type Boolean and its default value is false - The createAt is of type DateTime and when a new record is inserted, set it with the current date time - The updatedAt is of type DateTime and when a record is updated, update it with the current date time (`@updatedAt`) - Call the table "posts" (`@@map("posts")`) Well, nothing so difficult, just some special syntax to remember. I don't want to go too deep but If you are interested in the Data model of Prisma [in this reference](https://www.prisma.io/docs/concepts/components/prisma-schema/data-model#defining-models) the link to the documentation. _N.B. It's important to know well the mapping between the Prisma's types and the native's types of the different database services_ But let's go ahead, you described your entity now I think you want use it. To use this entity inside of your typescript code, you need to create its typescript definition. To do this, run the following code: ``` npx prisma generate ``` This command adds in you package.json a new dependence `@prisma/client`, and creates a folder `.prisma` inside of your node_modules folder. The `@prisma/client` is the package that contains the client to connect to your database, whereas, the .prisma folder contains a file `index.d.ts` that describes the post Entity and all the possible functions to work with this Entity. Ok, at this point, let's try to get data from this collection of `posts` and to do this let's create a file called `index.ts` inside the src folder and copy the following code. (`src/index.ts`) ```ts import { PrismaClient } from "@prisma/client"; async function main() { const prisma = new PrismaClient(); try { const posts = await prisma.post.findMany(); console.log({ posts }); } finally { prisma.$disconnect(); } } main(); ``` In this simple code, you created a connection to your database, found the posts inside the posts collections, and logged the result in the console. To run this code you have to add this section in your `package.json` file. ```json ... "scripts": { "dev": "ts-node ./src/index.ts" }, ... ``` This script permits you to run the code written above. Ok, it's time to show the result. Run in your terminal the following script. ``` yarn dev ``` ![Cat Oh no](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hs567zh06u7hyl20vv4r.jpg) Ok, maybe that's not the result that you expected but something's gone πŸ˜… The error that you are seeing in your terminal appeared because we haven't a database.... we haven't a database yet πŸ˜ƒ In the next article, I'll show you how to create the database and we'll resolve this terrible mistake, I promise! I think that could be all for today! See you soon Guys Bye Bye πŸ‘‹ _The code of this article is available [in the GitHub repository](https://github.com/Puppo/it-s-prisma-time/tree/03-create-table)_ --- ### Part 4 of 16: It's Prisma Time - Create Db - **URL**: https://delpuppo.net/blog/it-s-prisma-time/its-prisma-time-create-db/ - **Published**: Dec 23, 2021 - **Tags**: javascript, typescript, database, orm Hi Guys πŸ‘‹ welcome back. Finally, today it's time to create our database, so don't waste time, and get started. How can you create your database? It's simple! You have to run the following script: ``` npx prisma db push ``` This script creates in your `prisma` folder a SQLite database called `dev.db`. The database's name is taken from the variable `DATABASE_URL` in the `.env` file and inside of the file you can find the next table. ![Post Table](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/omgblt1n17m43g3ye3pz.png) Ok, now you have your database so it's time to get back at the challenge left open last article. Run the script ``` yarn dev ``` ```console { posts: [] } ``` ![it's work](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/va4l54jb21hltymty4ow.jpeg) Ok, there aren't data but it works πŸ₯³ So now you have a database and you can search data inside it, at this point, it's time to create relations between our entities, but it's the goal of the next article πŸ€ͺ That's all for today guys! See you soon Bye Bye πŸ‘‹ _You can find the code relative to this post [in the GitHub repository](https://github.com/Puppo/it-s-prisma-time/tree/04-create-db)_ --- ### Part 5 of 16: It's Prisma Time - Create Relations - **URL**: https://delpuppo.net/blog/it-s-prisma-time/its-prisma-time-create-relations/ - **Published**: Dec 28, 2021 - **Tags**: javascript, typescript, database, orm Hi Folks πŸ‘‹ today I'll show you how to create relations among your entities. So don't waste time and let's start! Before moving on, let me explain what we are going to do today. 1. Create a relation 1-N among posts and comments 2. Create a relation N-N among posts and authors 3. Explain a relation 1-1 So get started from the first one. ## 1-N To create the relation among Posts and Comments see the next code ```ts model Post { ... comments Comment[] } model Comment { id Int @id @default(autoincrement()) text String post Post @relation(fields: [postId], references: [id], onDelete: NoAction, onUpdate: Cascade) postId Int } ``` As you can see in this example there are 3 steps 1. Creation of the comment entity 2. Creation of the relation between Post and Comment (`@relation`) 3. Addition of the comments collection in the Post entity Let's focus on the second point now ```ts post Post @relation(fields: [postId], references: [id], onDelete: NoAction, onUpdate: Cascade) ``` this line of code says Prisma that in Comment entity there is a field `postId` related to the field `id` in `Post` entity. In this way you described the relation between Post and Comment entities. It's also possibile to describe what to do when a foreign key is deleted or updated using the keyword `onDelete` and `onUpdate`. This two commands could have 5 possibile values: - [Cascade](https://www.prisma.io/docs/concepts/components/prisma-schema/relations/referential-actions#cascade) - [NoAction](https://www.prisma.io/docs/concepts/components/prisma-schema/relations/referential-actions#noaction) - [Restrict](https://www.prisma.io/docs/concepts/components/prisma-schema/relations/referential-actions#restrict) - [SetDefault](https://www.prisma.io/docs/concepts/components/prisma-schema/relations/referential-actions#setdefault) - [SetNull](https://www.prisma.io/docs/concepts/components/prisma-schema/relations/referential-actions#setnull) Not all of them are guaranteed in all the databases connector, but you can find [in this reference](https://www.prisma.io/docs/concepts/components/prisma-schema/relations/referential-actions#types-of-referential-actions) all the info about that. Let's move on to the third point where you added this line of code ```ts comments Comment[] ``` this line create a collection of comments inside the Post Entity like in every ORM that respects itself, so you can retrive by code the comments related to your post. But let's move on and see the next relation. _N.B. it's possible to create a not mandatory foreign key too. In our context it makes no sense, but imagine that a comment should exist also without a Post. In order to map this relation you need to change the Comment entity in this way_ ``` model Comment { ... post Post? @relation(fields: [postId], references: [id]) postId Int? ... } ``` _Using the `?` character, you should indicate to Prisma that the field can be null._ ## N-N See the following code ``` model Post { id Int @id @default(autoincrement()) title String content String published Boolean comments Comment[] authors AuthorsOnPost[] } model Author { id Int @id @default(autoincrement()) firstName String lastName String comments Comment[] posts AuthorsOnPost[] } model AuthorsOnPost { author Author @relation(fields: [authorId], references: [id]) authorId Int post Post @relation(fields: [postId], references: [id]) postId Int @@id([authorId, postId]) } ``` As you can see there are 5 steps 1. Creation of the Author entity 2. Creation of the AuthorsOnPost entity 3. Creation of the relation between Posts and Authors using the AuthorsOnPost entity 4. Adding the collection of AuthorsOnPost in the Post Entity 5. Adding the collection of AuthorsOnPost in the Authors Entity Let's start to see the second point where you created a new Entity with two fields: `authorId` and `postId`. `authorId` is a foreign key related to the Author Entity whereas `postId` is a foreign key related to the Post Entity, I think if you understood the previous paragraph, you don't have any problem to understand this. Using the `@@id` you indicated to prisma that the AuthorsOnPost had a primary key composed by this two fields, so you can have the same author and the same post only once. By doing that, you have created a relation N-N between Authors and Posts, so in your database now, you can have a Post with multiple Authors and an Author can contribute to multiple Posts. The last two steps instead allow you to have the collection of Authors related to a Post and viceversa. ## 1-1 For this particular relation I hope you have understood the syntax so I'll leave a symbolic example. We can say that you have a Post that can be in relation only with an Author and viceversa, you have to describe that relation like this ``` model Post { ... author Author @relation(fields: [authorId], references: [id]) authorId Int } model Author { ..... post Post? } ``` I think it's not so hard to understand what I explained before, but it was necessary to bring this example too. I think that's everything from the Relation's world with Prisma, but before updating the database, let me add to the project a package that creates the ER diagram of your database. 1. Install the package `prisma-erd-generator` ``` yarn add -D prisma-erd-generator ``` 2. update the `prisma/schema.prisma` with the following code ``` generator client { provider = "prisma-client-js" } generator erd { provider = "prisma-erd-generator" } ... ``` Now when you call the script `npx prisma generate`, Prisma generates for you also a file `prisma/ERD.svg` where you can see your ER Diagram. Now it's time to upgrade your database and your types, so execute the next two scripts in your terminal ``` npx prisma generate npx prisma db push ``` Now your db is up to date and you have a new file like the next where there is your ER Diagram. ![Prisma ERD](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bacxr48dz3u4lfoyyo4o.png) That's all for today guys, in the next article it's time to see how you can insert data in you table. Before the greetings, if you are interested in seeing the prisma-erd-generator package you can find the repository [in the GitHub repository](https://github.com/keonik/prisma-erd-generator) and if you want to leave a ⭐, press the button πŸ˜‰ Ok, see you soon Guys Bye Bye πŸ‘‹ _The code relatives to this article is available [in the GitHub repository](https://github.com/Puppo/it-s-prisma-time/tree/05-create-relations)_ --- ### Part 6 of 16: It's Prisma Time - Insert - **URL**: https://delpuppo.net/blog/it-s-prisma-time/its-prisma-time-insert/ - **Published**: Dec 30, 2021 - **Tags**: javascript, typescript, database, orm Welcome Guys, Today it's time to add some records to the database 😁 so don't waste time and get your hand dirty. Let's start from a simple insert that adds an author, in your `src/index.ts` copy the next example. ```ts import { PrismaClient } from "@prisma/client"; async function main() { const prisma = new PrismaClient(); try { const newAuthor = await prisma.author.create({ data: { firstName: "John", lastName: "Doe", }, }); console.log({ newAuthor }); } catch (error) { console.error(error); throw error; } finally { await prisma.$disconnect(); } } main(); ``` In this code you created a simple insert that adds an author. By using the patter `prisma.[entity].create` you can insert in your db your entities, but let's see this code in action executing the next two script ``` npx prisma db push yarn dev ``` ```json { newAuthor: { id: 1, firstName: 'John', lastName: 'Doe' } } ``` As you can see it's not so hard to insert a record, but it's time to take the insert to the next level. Imagine that you want to insert a Post with one Comment in a single execution, how can you do that? Well, add to the previous code the next one, below the `console.log({ newAuthor });`. ```ts ... const newPost = await prisma.post.create({ data: { title: "First Post", content: "This is the first post", published: false, comments: { create: { text: "First comment", author: { connect: { id: newAuthor.id, }, }, }, }, }, include: { comments: true, }, }); console.log("newPost", JSON.stringify(newPost, null, 4)); ... ``` And now run the code using ``` npx prisma db push yarn dev ``` after that you can see this result ``` newPost { "id": 7, "title": "First Post", "content": "This is the first post", "published": false, "createAt": "2021-12-18T12:29:20.982Z", "updatedAt": "2021-12-18T12:29:20.982Z", "comments": [ { "id": 7, "text": "First comment", "postId": 7, "authorId": 7 } ] } ``` But what happened? By running this code you added in you database a post and a comment related to this post using a single command. I think you agree with me that it is a common feature in an ORM. But let's see better the create method and its parameters. Let's start from the `data` field, this field allows you to indicate all the fields related to your entity, in this case the Post Entity. When I say the entity's fields I am referring to the own fields but also to the fields of its related Entities as you did with the Comment Entity in the previous example. But let's move on and see another particularity in this example. To add the comment related to your post you used this snippet of code ```ts ... comments: { create: { text: "First comment", author: { connect: { id: newAuthor.id, }, }, }, }, ... ``` The first thing that comes to our attention is the create field. This field is not a field of the Comment Entity but is a command for Prisma. Using the [create](https://www.prisma.io/docs/concepts/components/prisma-client/relation-queries#create-a-related-record) command you are indicating to Prisma that you want to insert a new record in the comment table. The data relative to the comment are indicated inside of the create object just described. Okay but let’s look into this object where there is another peculiarity inside the field author. As you can see inside it there is a field called `connect`, but what is it? The [connect](https://www.prisma.io/docs/concepts/components/prisma-client/relation-queries#connect-an-existing-record) field is another command for Prisma. This command indicates to Prisma that the Author's record already exists in the database and it must not create it but it only needs to create the link between the comment record and the author record. There is another command for Prisma to manage the insert of an entities and it is `connectOrCreate`. This command allows us to check if the relative record exists and in this case Prisma creates the link between the entities, otherwise if it doesn't exist Prisma creates also this new record. To give you an example, here, the author connect command rewrites with the `connectOrCreate`. ```ts author: { connectOrCreate: { create: { lastName: "Last name", firstName: "First name", }, where: { id: newAuthor.id, }, }, }, ``` When you insert a new record, all the crud operations in your database are made under a transaction, so if in your insertion you have an entity with 3 sub-entities, you'll get the success result only if all operations will be successful, otherwise you'll get an error and your database will be left clean as before the execution. There is also a `createMany` method that allows you to do a bulk insert inside a collection. It isn't so different from the `create` method. I leave you the link to the documentation [in this reference](https://www.prisma.io/docs/concepts/components/prisma-client/crud#create-multiple-records). _N.B. createMany isn't supported in SQLite, Prisma helps you on that and when generates the definitions of the client, it detects the current connector and generates only the definitions supported by the connector_ Last but not least, Prisma exposes you a specific type for typescript that describes how you can call the create method in a type safe context. Next the author insert reviews with this type ```ts import { Prisma, PrismaClient } from "@prisma/client"; ... const authorCreate: Prisma.AuthorCreateArgs = { data: { firstName: "John", lastName: "Doe", }, }; const newAuthor = await prisma.author.create(authorCreate); ... ``` Prisma does this work for all the entities that you described in the `prisma.schema` file and it doesn't just make this, but we will go deep into this in the next articles. I think today you have got many notions about the insert with prisma. If you are interested in go in depth on the insert with prisma [in this reference](https://www.prisma.io/docs/concepts/components/prisma-client/crud#create) the link to the official documentation. That’s it guys. In the next article we’re going to see the delete operation. See you soon. Bye Bye πŸ‘‹ _You can find the code of this article [in the GitHub repository](https://github.com/Puppo/it-s-prisma-time/tree/06-insert)_ --- ### Part 7 of 16: It's Prisma Time - Delete - **URL**: https://delpuppo.net/blog/it-s-prisma-time/its-prisma-time-delete/ - **Published**: Jan 5, 2022 - **Tags**: javascript, typescript, database, orm Hi Guys πŸ‘‹ today we are going to see the delete operation. _Before moving to the delete operation, in the code of this article I added the next snippet of code at the beginning, so every time you run the code you have some records in your database._ ```ts const authors = await Promise.all( [1, 2, 3].map( async i => await prisma.author.create({ data: { firstName: `First name ${i}`, lastName: `Last name ${i}`, }, }) ) ); ``` The delete operation in Prisma can be done in two ways: the deletion of one record and the deletion of multiple records. Let's start from the first one and let's see the next code ```ts const deletedAuthor = await prisma.author.delete({ where: { id: authors[0].id, }, }); ``` As you can see, the delete method is quite simple, you need to follow this pattern `prisma.[entity].delete`, the where field must have the primary key of the record that has to be deleted. It's important to remember that if this method doesn't find a record to delete, throws an Exception that you can handle in this way ```ts try { const deletedAuthor = await prisma.author.delete({ where: { id: 1, }, }); console.log({ deletedAuthor }); } catch (error) { if ( error instanceof Prisma.PrismaClientKnownRequestError && error.code === "P2025" ) { console.log("Author not found"); } else console.error(error); } ``` _If you are interested in seeing all the possibile errors returned by Prisma you can find the documentation [in this reference](https://www.prisma.io/docs/reference/api-reference/error-reference)_ Let's bring the delete to the next level and let's see the deleteMany method. I think you already have understood that this method allows you to delete multiple records in one step, but let me show you an example. ```ts const deletedAuthorsResult = await prisma.author.deleteMany({ where: { id: { in: authors.map(a => a.id), } }, }); ``` As you can see, this method isn't so different from the delete, but the differences are in the where field and in this method you can filter the datas like in a SQL where. In this particular case the code deletes the records that have the id contained in the array passed by the authors collection. Another difference between the deleteMany and the delete is the result's type. The deleteMany returns an object with a single property called `count` and it contains the amount of the records effected by the delete operation. Now, I would spend some words about the "where" because it exposes a lot of possibilities to search the records that we want to delete. Thus I want to start this explanation from the "author where"'s typescript definition. ```ts export type AuthorWhereInput = { id?: IntFilter | number firstName?: StringFilter | string lastName?: StringFilter | string comments?: CommentListRelationFilter posts?: AuthorsOnPostListRelationFilter AND?: Enumerable OR?: Enumerable NOT?: Enumerable } ``` As you can see, this type has some strange things, but if you are familiar with the SQL, I think you have an idea of what you can do. Don't waste time and let's start from the `id`. The id field can have two values, either a number or `IntFilter`. At this point, if you need to find your record using the id, you can pass your value to this field, else if you want to delete records using more complex search terms you can use the `IntFilter`, which is composed thus ```ts export type IntFilter = { equals?: number in?: Enumerable notIn?: Enumerable lt?: number lte?: number gt?: number gte?: number not?: NestedIntFilter | number } ``` As you can image this type represents the common operations that you can do in SQL with the numbers: - equals: checks if the value is equal to the passed value - in: checks if the value is inside of the list of numbers that you pass - notIn: is the opposite of `in` - lt: checks if the value is less than the passed value - lte: checks if the value is less or equal to the passed value - gt: checks if the value is greater than the passed value - gte: checks if the value is greater or equal to the passed value - not: checks if a value is not of the passed value, or you can also set it with the type `NestedIntFilter`, which is another type equal to the `IntFilter`, and it allows you to create your filters using the positive search terms and check if the records respect the opposite. As you may imagine, there are other three types similar to: StringFilter, BoolFilter and DateTimeFilter, and each one depends from the column's type that you are testing. DateTimeFilter is equal to the IntFilter expect that works with the Date. BoolFilter contains only the `equals` and the `not` operations for obvious reasons. The StringFilter in addition to the operations exposed by the IntFilter has other three operations: `contains`, `startsWith` and `endsWith` (I won't describe these operations because I think they speak by themselves). After this explanation, I think you have understood also the fields: `firstName`, `lastName`. Now I want to go on to the `posts` and `comments` fields, that have the same approach. These fields allow you to check some particular cases inside the posts or the comments related to the record. Each field allows you to do these three operations `every`, `some` and `none` ```ts export type CommentListRelationFilter = { every?: CommentWhereInput some?: CommentWhereInput none?: CommentWhereInput } export type AuthorsOnPostListRelationFilter = { every?: AuthorsOnPostWhereInput some?: AuthorsOnPostWhereInput none?: AuthorsOnPostWhereInput } ``` Using these filters you can check inside of the posts or the comments relative to the Author, so you can check: - if `every` post/comment has a particular value or more particular values - if `some` post/comment has a particular value or more particular values - if `none` post/comment has a particular value or more particular values It's not so difficult to understand it but I recommend you to play with them. Now it's time to see the last three operations: `AND`, `OR` and `NOT`. I think you already understood their goals, but let me spend some words. These three operations let you to combine multiple filters using the operation AND OR NOT. You can combine together these operations and you can also nest them. For instance, you can use the AND and the OR in the first level of your filter, and use the NOT and the OR inside the AND filter. I think an example can give the idea. ```ts const deletedAuthorsResult = await prisma.author.deleteMany({ where: { AND: [ { NOT: { id: 10, }, }, { OR: { firstName: { startsWith: "name", }, }, }, ], OR: [ { posts: { every: { post: { updatedAt: { lt: new Date(Date.now() - 1000 * 60 * 60 * 24 * 7), }, }, }, }, }, ], }, }); ``` As you can see, you can do every type of check in you delete using the `deleteMany` and I think the definition types created by the Prisma team are a good friends to prevent errors. It's obvious that they don't create the right query for you, that's your work. πŸ’ͺ That's all for today guys! I hope I let you a good introduction about the delete in prisma and that now you are ready to go to try it by yourself. In the next article it's time to speak about the update operation, so see you soon folks! Bye Bye! πŸ‘‹ _The code of this article is available [in the GitHub repository](https://github.com/Puppo/it-s-prisma-time/tree/07-delete)_ --- ### Part 8 of 16: It's Prisma Time - Update - **URL**: https://delpuppo.net/blog/it-s-prisma-time/its-prisma-time-update/ - **Published**: Jan 7, 2022 - **Tags**: javascript, typescript, database, orm Hi Folks πŸ‘‹ Today it's time to learn something about the update operation, so don't waste time and let's go! The update method in Prisma is not that different from the insert. Let's give an example. Starting from the code of the previous article, you can remove all the code relative to the delete operation and after the insert you can add the following code. ```ts const updatedAuthor = await prisma.author.update({ data: { firstName: "Updated first name", lastName: "Updated last name", }, where: { id: authors[0].id, }, }); console.log({ updatedAuthor }); ``` As you can see, the update method used the patter `prisma.[entity].update`, not so different from the insert and the delete, obviously the update method updates an existing row. And another thing, if the update method doesn't find the record, it throws an exception that you have to handle in your code. If you execute that code you get this result. ```json { updatedAuthor: { id: 3, firstName: 'Updated first name', lastName: 'Updated last name' } } ``` I think this operation is easy to understand, but now let me show you some special features that the update method exposes. In some cases, when we want to update a row, we don't want to set a new value, but we want to `increment`, `decrement`, `multiply` or `divide` a field in a [atomic update operation](https://www.prisma.io/docs/reference/api-reference/prisma-client-reference#atomic-number-operations). To do this Prisma exposes us these commands in the type `IntFieldUpdateOperationsInput` ```ts export type IntFieldUpdateOperationsInput = { set?: number increment?: number decrement?: number multiply?: number divide?: number } ``` and we can use it in this way ```ts const updatedAuthor = await prisma.author.update({ data: { id: { increment: 1 } }, where: { id: authors[0].id, }, }); ``` Obviously, incrementing the primary key doesn't have sense, but this is just a demonstrative example. Let's go on and see another important feature exposed by the update operation, to work with the relative collections. In the insert operation, we saw that when we want to insert a new record, we can use [create](https://www.prisma.io/docs/concepts/components/prisma-client/relation-queries#create-a-related-record), [connect](https://www.prisma.io/docs/concepts/components/prisma-client/relation-queries#connect-an-existing-record) or [connectOrCreate](https://www.prisma.io/docs/concepts/components/prisma-client/relation-queries#connect-or-create-a-record) operation to create a relation between two records. In the update operation in addition we have: - [upsert](https://www.prisma.io/docs/concepts/components/prisma-client/relation-queries#update-or-create-a-related-record): update or create a related record - [set](https://www.prisma.io/docs/concepts/components/prisma-client/relation-queries#disconnect-all-related-records): connect the record with an existing records, _(if you set this field with an empty you disconnect all record)_ - [disconnect](https://www.prisma.io/docs/concepts/components/prisma-client/relation-queries#disconnect-a-related-record): remove the relation with existing records - [delete](https://www.prisma.io/docs/concepts/components/prisma-client/relation-queries#delete-specific-related-records): delete the record in relation with it - [update](https://www.prisma.io/docs/concepts/components/prisma-client/relation-queries#update-a-specific-related-record): update the related record - [updateMany](https://www.prisma.io/docs/concepts/components/prisma-client/relation-queries#update-all-related-records-or-filter): update the related records - [deleteMany](https://www.prisma.io/docs/concepts/components/prisma-client/relation-queries#delete-all-related-records): delete the related records I let you the official document for each type, so you can deepen if you are interested. As you can imagine, over the update operation there is the updateMany operation. In this article I prefer not to look into this operation because I have already left you many info about the update. But let me spend some words about it, so you can try it by yourself. The updateMany operation combine the `where` seen in the deleteMany operation with the features explained above, so it's obvious to understand that this operation allows you to update many records, using a complex filter. It's all for today guys. In the next article it's time to see the select operation. See you soon Bye Bye πŸ‘‹ _You can find the code of this article [in the GitHub repository](https://github.com/Puppo/it-s-prisma-time/tree/08-update)_ --- ### Part 9 of 16: It's Prisma Time - Select - **URL**: https://delpuppo.net/blog/it-s-prisma-time/its-prisma-time-select/ - **Published**: Jan 11, 2022 - **Tags**: javascript, typescript, database, orm Welcome back Guys, Today it's time to get our record using the select command. Let's start with a simple example (copy and paste the setup method in the [repository]() if you want some record in your database). ```ts const prisma = new PrismaClient(); const posts = await prisma.post.findMany(); console.log(JSON.stringify(posts, undefined, 2)); ``` As you can see, to do the select in Prisma we need to use the [findMany](https://www.prisma.io/docs/reference/api-reference/prisma-client-reference#findmany) method. In this case, the code does a simple select without anything else (no where, no groupBy, no orderBy...), it's like doing this query ```sql SELECT * FROM POSTS ``` But let's move on and add a WHERE clause to our select. ```ts ... const posts = await prisma.post.findMany({ where: { id: { gte: 5, }, }, }); ... ``` The where clause is the same seen in the previous articles (delete and update), so I won't go in depth because I've already spent many words about it. Therefore let's see the order by command ```ts const posts = await prisma.post.findMany({ orderBy: { published: "desc", }, }); ``` As you can see, the orderBy command is so simple, you need to create an object and indicate the column used for the sorting and its direction ("desc" or "asc"). But if you want to indicate multiple columns in your sorting, the syntax is a little bit different. Let's see it in this code. ```ts const posts = await prisma.post.findMany({ orderBy: [{ published: "desc" }, { createAt: "asc" }], }); ``` As you can tell, when you need to indicate multiple columns, the orderBy field wants an array of values, and each value must contains the column name and its direction. But let's go on and see how to indicate only a subset of columns of our entity. Supposing that we want to select only the fields `id`, `title` and `content` of the Post Entity, how can we do that? Fortunately, in the findMany method there is a configuration to indicate that, let's see it the next code. ```ts const posts = await prisma.post.findMany({ select: { id: true, title: true, content: true, }, }); ``` As you can notice, using the select option, we can indicate the column we want to extract, but Prisma doesn't end here its works. When we use the select option, Prisma returns an object that respects our selection, so the typescript compiler can detect the right fields of the objects. In this case the `posts` collection has this type ```ts const posts: { id: number; title: string; content: string; }[] ``` It's fantastic I think, isn't it? Ok let's go on and see the `include` option. The include option is used to load the relative Entities too. Immagine you want get for each post its relative authors and its comments; using the include option it's possibile. Let's see how in the next code. ```ts const posts = await prisma.post.findMany({ include: { authors: { select: { author: true, }, }, comments: true, }, }); ``` As you can see, the include option allows us to get the authors and the comments relative to each post. The result of this query is ```json [ { "id": 1, "title": "Post title 2", "content": "Post content 2", "published": true, "createAt": "2022-01-03T08:52:30.690Z", "updatedAt": "2022-01-03T08:52:30.690Z", "authors": [ { "author": { "id": 4, "firstName": "First name 2", "lastName": "Last name 2" } } ], "comments": [] }, { "id": 2, "title": "Post title 5", "content": "Post content 5", "published": false, "createAt": "2022-01-03T08:52:30.690Z", "updatedAt": "2022-01-03T08:52:30.690Z", "authors": [ { "author": { "id": 3, "firstName": "First name 5", "lastName": "Last name 5" } } ], "comments": [] }, { "id": 3, "title": "Post title 1", "content": "Post content 1", "published": false, "createAt": "2022-01-03T08:52:30.690Z", "updatedAt": "2022-01-03T08:52:30.690Z", "authors": [ { "author": { "id": 5, "firstName": "First name 1", "lastName": "Last name 1" } } ], "comments": [] } ... ] ``` After the explanation of the `findMany` method, it's important to spend some words about other two methods: [findUnique](https://www.prisma.io/docs/reference/api-reference/prisma-client-reference#findunique) and [findFirst](https://www.prisma.io/docs/reference/api-reference/prisma-client-reference#findfirst). As you can immagine, these two methods allow us to retrieve a single record. The findFirst is like the findMany but it returns the first record of the result. The findUnique otherwise returns a record by its key. If they do not find any results return `null`. Let's see an example of both: _- findFirst_ ```ts const post = await prisma.post.findFirst({ where: { published: true, }, }); ``` _- findUnique ```ts const post = await prisma.post.findUnique({ where: { id: 1, }, }); ``` Ok, I think that's all for today. In the next article we are going to see how to paginate the results. See you soon guys! Bye Bye! _[in the GitHub repository](https://github.com/Puppo/it-s-prisma-time/tree/09-select) you can find the code of this article_ --- ### Part 10 of 16: It's Prisma Time - Pagination - **URL**: https://delpuppo.net/blog/it-s-prisma-time/its-prisma-time-pagination/ - **Published**: Jan 13, 2022 - **Tags**: javascript, typescript, database, orm Hi Guys πŸ‘‹ Today we are going to see how to paginate our results. So don't waste time and let's go! With Prisma there are two ways to paginate results: [Offset pagination](https://www.prisma.io/docs/concepts/components/prisma-client/pagination#offset-pagination) or [Cursor-based pagination](https://www.prisma.io/docs/concepts/components/prisma-client/pagination#cursor-based-pagination). What are the differences though? The first one is used when we need to create a sequence of pages and get their datas according to the page. The second one, instead, is used when we want the items after a specific record, to continue the list after that element. But let's see an example of both, starting with the first one `skip` and `take` ```ts { const pageOnePosts = await prisma.post.findMany({ take: 3, orderBy: { id: "asc", }, }); console.log(`Page 1: `, JSON.stringify(pageOnePosts, undefined, 2)); const pageTwoPosts = await prisma.post.findMany({ skip: 3, take: 3, orderBy: { id: "asc", }, }); console.log(`Page 2: `, JSON.stringify(pageTwoPosts, undefined, 2)); } ``` The result of this example is this ```json [ { "id": 1, "title": "Post title 1", "content": "Post content 1", "published": false, "createAt": "2022-01-03T10:14:51.274Z", "updatedAt": "2022-01-03T10:14:51.274Z" }, { "id": 2, "title": "Post title 8", "content": "Post content 8", "published": true, "createAt": "2022-01-03T10:14:51.274Z", "updatedAt": "2022-01-03T10:14:51.274Z" }, { "id": 3, "title": "Post title 4", "content": "Post content 4", "published": true, "createAt": "2022-01-03T10:14:51.274Z", "updatedAt": "2022-01-03T10:14:51.274Z" } ] [ { "id": 4, "title": "Post title 10", "content": "Post content 10", "published": true, "createAt": "2022-01-03T10:14:51.274Z", "updatedAt": "2022-01-03T10:14:51.274Z" }, { "id": 5, "title": "Post title 9", "content": "Post content 9", "published": false, "createAt": "2022-01-03T10:14:51.274Z", "updatedAt": "2022-01-03T10:14:51.274Z" }, { "id": 6, "title": "Post title 6", "content": "Post content 6", "published": true, "createAt": "2022-01-03T10:14:51.274Z", "updatedAt": "2022-01-03T10:14:51.274Z" } ] ``` As you can see, the first `findMany` has the orderBy and the take options. The orderBy is used to give a sort to our results and the take option is used to get the first 3 elements of the results. In this case the skip option isn't indicated so its value is 0 (default value). Instead, in the second `findMany` method there is also the skip option, and it's indicated with the value 3. In this case, the findMany method returns 3 elements (take: 3) after the third element (skip: 3), so we can see the results of the second page. I think it's not so difficult to understand it, so let's go on to the cursor implementation. Let's start from the code ```ts const pageOnePosts = await prisma.post.findMany({ take: 3, orderBy: { id: "asc", }, }); console.log(`Page 1: `, JSON.stringify(pageOnePosts, undefined, 2)); const pageTwoPosts = await prisma.post.findMany({ skip: 1, take: 3, cursor: { id: pageOnePosts[pageOnePosts.length - 1].id, }, orderBy: { id: "asc", }, }); console.log(`Page 2: `, JSON.stringify(pageTwoPosts, undefined, 2)); ``` The results ```json [ { "id": 1, "title": "Post title 1", "content": "Post content 1", "published": false, "createAt": "2022-01-03T10:14:51.274Z", "updatedAt": "2022-01-03T10:14:51.274Z" }, { "id": 2, "title": "Post title 8", "content": "Post content 8", "published": true, "createAt": "2022-01-03T10:14:51.274Z", "updatedAt": "2022-01-03T10:14:51.274Z" }, { "id": 3, "title": "Post title 4", "content": "Post content 4", "published": true, "createAt": "2022-01-03T10:14:51.274Z", "updatedAt": "2022-01-03T10:14:51.274Z" } ] [ { "id": 4, "title": "Post title 10", "content": "Post content 10", "published": true, "createAt": "2022-01-03T10:14:51.274Z", "updatedAt": "2022-01-03T10:14:51.274Z" }, { "id": 5, "title": "Post title 9", "content": "Post content 9", "published": false, "createAt": "2022-01-03T10:14:51.274Z", "updatedAt": "2022-01-03T10:14:51.274Z" }, { "id": 6, "title": "Post title 6", "content": "Post content 6", "published": true, "createAt": "2022-01-03T10:14:51.274Z", "updatedAt": "2022-01-03T10:14:51.274Z" } ] ``` As you may notice, the results are the same of the previous example but the implementation is a little bit different in the second part. In this case, the second findMany has the same orderBy and take options as in the first example, but it has a different skip and uses the cursor option. First let's focus on the cursor option. This option allows us to indicate the last item returns by the previous search, so we can restart our pagination from this element and combining it with the `skip: 1`, we'll get the results after this element. *It's important to indicate the skip option with the one value, otherwise the element indicates in the cursor is part of the results.* As you can tell, both implementations are correct but each of them resolve a different type of pagination. Now it's your job to take the right one. Ok guys that's all! I hope now you have an idea of how you can paginate your datas using prisma. In the next article, we are going to see how to aggregate and group the datas. See you soon! Bye Bye πŸ‘‹ _[in the GitHub repository](https://github.com/Puppo/it-s-prisma-time/tree/10-pagination) you can find the code of this article._ --- ### Part 11 of 16: It's Prisma Time - Aggregate and GroupBy - **URL**: https://delpuppo.net/blog/it-s-prisma-time/its-prisma-time-aggregate-and-groupby/ - **Published**: Jan 18, 2022 - **Tags**: javascript, typescript, database, orm Welcome back Folks πŸ‘‹ Today we are seeing how to aggregate and group data. Let's start from the first one. To aggregate data in Prisma you have to use the [aggregate](https://www.prisma.io/docs/reference/api-reference/prisma-client-reference#aggregate) method. Using this method, you can aggregate data of type number, and you can do these operations: - max - min - count - sum - avg _N.B. I won't spend time explaining what these operations do because I think their names speak for themselves_ Therefore, let's give an example to see the aggregate method at work. ```ts const aggregate = await prisma.author.aggregate({ _avg: { age: true, }, _max: { age: true, }, _min: { age: true, }, }); ``` As you can see, in this example, we aggregated the authors and we got as result the authors' ages average, the age of the older author and the age of the younger author. The result is composed in this way: ```ts { _avg: { age: true; }; _max: { age: true; }; _min: { age: true; }; } ``` In addition, the aggregate method allows us to use these operations: - where - orderBy - take - skip - cursor So as you can immagine, you can filter and sort your data before aggregate them. I think this feature might be clear that way, so let's move on to the groupBy feature. The [groupBy](https://www.prisma.io/docs/reference/api-reference/prisma-client-reference#groupby) feature allows us to group data by one o more fields. Let’s see an example to start ```ts const commentsGroupByPost = await prisma.comment.groupBy({ by: ["postId"], _count: { authorId: true, _all: true, }, orderBy: { _count: { authorId: "desc", }, }, having: { authorId: { _count: { gt: 1, }, }, }, }); ``` In this example, we grouped the comments by post, and after that, we counted the number of comments with the author and the number of comments for that post. Moreover, we sorted the result by the authors' count and, using the having option, we took only the posts that have at least a comment with an author. As you can see, this method is not difficult to use and understand. _It's also possibile to filter the data with the `where` option._ I think for today that's all. We saw how to aggregate and how to group data using Prisma and, if you want to deepen, the prisma team created this [video](https://www.youtube.com/watch?v=BdlCPdPaorY) to explain better the groupBy feature. In the next article we will see how to run our queries under transaction, but for today it's time to close πŸ˜ƒ See you soon guys Bye bye πŸ‘‹ _To show the code of this article in action follow this [in the GitHub repository](https://github.com/puppo/it-s-prisma-time/tree/11-grouping-and-aggregate)_ --- ### Part 12 of 16: It's Prisma Time - Transactions - **URL**: https://delpuppo.net/blog/it-s-prisma-time/its-prisma-time-transactions/ - **Published**: Jan 20, 2022 - **Tags**: javascript, typescript, database, orm Hello Guys πŸ‘‹ Today it's time to speak about transactions. Transactions are a pillar of the database world, and they help us to make consistency in the data when we work with multiple entities. So, can Prisma not handle them? Obviously the answer is no πŸ˜ƒ In this moment, Prisma handles the transactions in two ways, the first one is the official feature and the second one is in preview. Let's start from the first one. The first method to do a transaction in Prisma is using the `$transaction` method. This method accepts a list of operations that are performed in a single transaction. If all of these operations are successful the transaction does the commit otherwise it does the rollback. Let's see this method at work. ```ts const result = await prisma.$transaction([ prisma.author.create({ data: { firstName: "Author from transaction", lastName: "Author from transaction", age: getRandomInt(16, 100), }, }), prisma.post.create({ data: { title: "Post from transaction", content: "Post from transaction", published: false, }, }), ]); ``` The result of this operation is an array of elements, where the elements are the result of each operation in the transaction, in our example the result is composed in this way `[Author, Post]`. As you can imagine, this type of transaction is a good solution if you need to create a bulk insert or if you need to create a list of entities not relative to each other. Therefore, to solve this problem, the Prisma team is working to improve the transaction method. In this time, we can try this feature in preview, enabling it in the `schema.prisma` file. First of all, open the `schema.prisma` file and update the generator client in this way ```ts generator client { provider = "prisma-client-js" previewFeatures = ["interactiveTransactions"] } ``` _N.B. since [4.7.0](https://github.com/prisma/prisma/releases/tag/4.7.0), this feature has been production ready, so you can remove the `"interactiveTransactions"` flag._ after that it's necessary to update the prisma definitions, so run in your terminal this command ``` npx prisma generate ``` By doing this, we enabled the feature. Now, we'll see the previous example, rewritten in the way that the author and post are in relation. Let's see the result ```ts const result = await prisma.$transaction(async prisma => { const authorData = { firstName: "Author from transaction", lastName: "Author from transaction", age: getRandomInt(16, 100), } as const; const author = await prisma.author.create({ data: authorData, }); const post = await prisma.post.create({ data: { title: "Post from transaction", content: "Post from transaction", published: false, authors: { create: [ { authorId: author.id, }, ], }, }, include: { authors: { include: { author: true, }, }, }, }); return { author, post }; }); ``` As you can see, this feature allows us to handle the transaction like a function, and in this function we can do all the operations that we want to guarantee under transaction. We can also create an entity by using the create method and await its result. After that we can use this result to create or update another entity always under transaction. If you want to roll back the transaction because there are some inconsistent data, you need to throw an exception. I think for today that's all, but before I let you go, I suggest you this [reading](https://www.prisma.io/docs/guides/performance-and-optimization/prisma-client-transactions-guide) about transactions in the prisma site. It explains very well how this feature works and how to handle it in the best way. In the next article, we are going to see how to log the queries executed by Prisma, but now it's time to say goodbye πŸ˜ƒ See you soon Guys Bye bye πŸ‘‹ _You can find the code of this article [in the GitHub repository](https://github.com/puppo/it-s-prisma-time/tree/12-transaction)_ --- ### Part 13 of 16: It's Prisma Time - Logging - **URL**: https://delpuppo.net/blog/it-s-prisma-time/its-prisma-time-logging/ - **Published**: Jan 26, 2022 - **Tags**: javascript, typescript, database, orm Hi Guys πŸ‘‹ Today we are going to see how to enable logging in Prisma, so don't waste time and let's start! In Prisma we have 4 type of logging - query: this level of log allows us to see the queries created by Prisma to perform our operations - info - warn - error _I think the last three levels of log don’t need a description if you’re a developer._ We can enable all of them or only those are necessary. By default these logs are written in the stdout, but we can also send them in an event and handle them as we prefer. _P.S. if you are in development mode, you can enable all of these log levels setting the DEBUG environment variable to true_ But let's see how to enable these logs. ```ts const prisma = new PrismaClient({ log: [ { emit: "event", level: "query", }, "info", "warn", "error", ], }); prisma.$on("query", e => { console.log("Query: " + e.query); console.log("Duration: " + e.duration + "ms"); }); ``` The log option accepts an array of log levels that can be simple strings (query, info, warn, or error) or objects composed of two fields: level and emit. The level field can have query, info, warn, or error as values; whereas the emit field can have two values: stdout or event. If the "emit" value is equal to "stdout", the result of this log level will be written in the console, otherwise, if the value is "event", the result must be handled by a subscriber. But let’s clarify and see how to manage these logs. ```ts const prisma = new PrismaClient({ log: [ { emit: "event", level: "query", }, "info", "warn", "error", ], }); prisma.$on("query", e => { console.log("Query: " + e.query); console.log("Duration: " + e.duration + "ms"); }); ``` In this snippet of code, you can see how to enable the log levels and how to handle a subscriber of a specific type of log level. As you can see, it's not difficult to enable logs but it's important to remember to do it in the right way in base of the environment where our software is running. It's also possibile, as you can tell, to subscribe to the events of a log using the `$on` method. By using this method you can send where you want all your logs. The last thing that I want to leave you is an example of a query logged in the console. ```console SELECT `main`.`posts`.`id`, `main`.`posts`.`title`, `main`.`posts`.`content`, `main`.`posts`.`published`, `main`.`posts`.`createAt`, `main`.`posts`.`updatedAt` FROM `main`.`posts` WHERE `main`.`posts`.`published` = ? LIMIT ? OFFSET ? ``` As you can see, you can get the real SQL query executed by Prisma, and you can copy and past it in other SQL tool to check its plan or everything else. In this way, we can check if we have a slowdown and if we need to write our own query. The How is the goal of the next article 😁 So that's all for now folks. In the next article, we are going to see how to execute own queries using Prisma. See you soon guys Bye Bye πŸ‘‹ _You can find the code of this article [in the GitHub repository](https://github.com/puppo/it-s-prisma-time/tree/13-logging-query)_ --- ### Part 14 of 16: It's Prisma Time - Execute your own queries - **URL**: https://delpuppo.net/blog/it-s-prisma-time/its-prisma-time-execute-your-own-queries/ - **Published**: Jan 28, 2022 - **Tags**: javascript, typescript, database, orm Hello Folks πŸ‘‹ Today it's time to see how we can execute our own queries. In prisma we can run two kind of custom queries: "Actionable" queries (INSERT, UPDATE or DELETE) and "Retrievable" queries (SELECT). The first one can be executed using the `$executeRaw` method while the other one can be executed using the `$queryRaw`. ## Method $executeRaw This method enables us to create custom queries to insert update or delete records in the database. Using the [tagged template](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals?retiredLocale=it#tagged_templates) we can write our query and Prisma runs it "magically". Let’s see it in action to understand it better ```ts const result = await prisma.$executeRaw` INSERT INTO posts (title, content, published, updatedAt) VALUES (${"Post Title"}, ${"Post Content"}, ${false}, ${new Date()});`; console.log(`Insert result: ${result}`); ``` As you can see, it's not so difficult to use this method. By using the Tagged Template we can write our query and pass all the parameters in complete safety. In complete safety? Yes, you are reading right. By using this method all the parameters passed with the syntax `${parameter}` are safe from the SQL-Injection, so we won't worry about these attacks. _You can read more about this [in this reference](https://www.prisma.io/docs/concepts/components/prisma-client/raw-database-access#sql-injection)_ The last thing that I want to leave you is the return value of this method. The method returns the number of records affected by our queries. Thus we can check if our queries are executed correctly. ## Method $queryRaw This method allows us to retrieve data using custom queries. Sometimes, we need more performance and the query created by Prisma must be rewritten, in this case the `$queryRaw` method is our lifeline. This method uses the Tagged Template and it prevents attack by SQL-injection too. The result of this method is always an array and the type of this array depends on the result of your query. To explain better this concept let me give an example. ```ts const result: Post[] = await prisma.$queryRaw` SELECT p.id, p.title, p.content, p.published, p.createAt, p.updatedAt FROM posts p WHERE p.published = ${true} ORDER BY p.createAt DESC`; result.forEach(post => { const { id, title, content, createAt, published, updatedAt } = post; console.log({ id, title, content, createAt, published, updatedAt, }); }); ``` If you look closely this piece of code, you can notice that the `result` variable is an array of type Post, and also the `$queryRaw` method indicates that as its return type. By doing so, we receive the right check by typescript in the following rows of code. But there is a thing to keep in mind. The conversion from the result of the query to your type is not guaranteed by Prisma, it's your business in this case. _N.B. If you don't indicate your result type, by default Prisma returns the [unknown](https://dev.to/this-is-learning/typescript-tips-tricks-any-vs-unknown-2ho6) type_ Let's move on and see another feature that we can adopt when we are using these methods. Sometimes we have to use the IN operator in a custom query. Probably you are thinking that using the `map` method of the array is the right thing, unfortunately it's not so. To do this, Prisma exposes us a specific method `Prisma.join`. This method builds for us the IN clause in a safety mode, so we can use it in our custom queries. Let's see it in action ```ts const posts = await prisma.$queryRaw< Post[] >`SELECT * FROM posts WHERE id IN (${Prisma.join([1, 2, 3])})`; console.log(`Posts in 1,2,3: ${JSON.stringify(posts, null, 2)}`); ``` As you can notice, in this piece of code the values in the IN clause are built using the `join` method, nothing so difficult but it's important to keep in mind to use it in these cases. Two other good features exposed by Prisma are the `Prisma.sql` and `Prisma.empty`. These features enable us to create dynamic queries. Imagine that your query needs to do a WHERE clause only if there is a filter. By combining these two features you can create a custom query that depends on the data that you receive. Let's see an example ```ts const author: string | undefined = "transaction"; const authors = await prisma.$queryRaw` SELECT * FROM authors a ${ !!author ? Prisma.sql`WHERE a.firstName || ' ' || a.lastName LIKE ${`%${author}%`}` : Prisma.empty }`; console.log(`Authors: ${JSON.stringify(authors, null, 2)}`); ``` As you can see, in this example we check the value of the author variable, and if it's defined the query will execute using the filter (Prisma.sql), otherwise the query will execute without the filter(Prisma.empty). Even here, nothing so difficult to understand but these are two really good methods to write our own queries better. Ok Guys, it's all for today. In the next article we are going to see how to use the migrations with Prisma. See you soon Bye Bye πŸ‘‹ _You can find the code of this article [in the GitHub repository](https://github.com/Puppo/it-s-prisma-time/tree/14-execute-your-queries)_ --- ### Part 15 of 16: It's Prisma Time - Migrations - **URL**: https://delpuppo.net/blog/it-s-prisma-time/its-prisma-time-migrations/ - **Published**: Feb 2, 2022 - **Tags**: javascript, typescript, database, orm Hello Guys πŸ‘‹, Today it's time to speak about migrations. Migrations in Prisma are managed by *Prisma Migrate*. Prisma Migrate is an imperative database schema migration tool that enables you to: - Keep your database schema in sync with your Prisma schema as it evolves and - Maintain existing data in your database To see how it works, today we'll recreate all the schema used in this series, so let's started. ## First migration Before creating our first migration, we need to create our schema, thus in the `prisma/schema.prisma` we will create the entities: Post, Author and AuthorsOnPost. To do that, we need to add in `prisma/schema.prisma` this code. ``` // This is your Prisma schema file, // learn more about it in the docs: https://pris.ly/d/prisma-schema generator client { provider = "prisma-client-js" } datasource db { provider = "sqlite" url = env("DATABASE_URL") } model Post { id Int @id @default(autoincrement()) title String content String published Boolean @default(false) createAt DateTime @default(now()) updatedAt DateTime @updatedAt authors AuthorsOnPost[] @@map("posts") } model Author { id Int @id @default(autoincrement()) firstName String lastName String age Int posts AuthorsOnPost[] @@map("authors") } model AuthorsOnPost { author Author @relation(fields: [authorId], references: [id]) authorId Int post Post @relation(fields: [postId], references: [id]) postId Int @@id([authorId, postId]) @@map("authors_on_post") } ``` Once this is done, we can generate our first migration using this command ```cli npx prisma migrate dev ``` This command generates our first migration for us. If you take a look at your project, you can notice that you have a new folder called migration inside the Prisma folder. Inside this folder, there is another folder whose name is the timestamp of when you have executed the migration command. The format is YYYYmmDDhhMMss. If you open this folder, you find a file called `migration.sql`, where you can see inside the scripts to generate the schema of your database. In this case, in this file, there are 3 CREATE TABLE commands, each for every entity (Author, Post, AuthorsOnPost). Prisma allows us to indicate migration's name too by using the `--name` option. Therefore the previous command could be executed in this way ```cli npx prisma migrate dev --name create_post_and_author_entities ``` By doing that, the migration's folder adds to its name this name immediately after the timestamp. Thus we have a migration with info about what it does. *Important* When we run the migrate command, Prisma updates our local database too. ## Second migration Now we want to add the Comment entity to our schema. First of all we have to create it in the Prisma Schema adding this code in the `prisma/schema.prisma` file ```prisma model Comment { id Int @id @default(autoincrement()) text String post Post @relation(fields: [postId], references: [id], onDelete: NoAction, onUpdate: Cascade) postId Int author Author? @relation(fields: [authorId], references: [id], onDelete: SetNull, onUpdate: Cascade) authorId Int? @@map("comments") } ``` Once this is done, we need to update the entities Author and Post adding the collections of comments. To do this, you need to add these lines of code ```prisma model Post { ... comments Comment[] ... } model Author { ... comments Comment[] ... } ``` Now it's time to create our second migration using the next command ```cli npx prisma migrate dev --name add_comment_entity ``` As you can see, now we have another folder in the migration folder that contains the last migration. ## Rename a field How we can notice, updating our schema is not so difficult using the migrate command, unless we have to rename one or more fields. When we have to rename a field, the Prisma Migrate tool works in this way 1. CREATE a new column 2. DROP the existing column and the data in that column as you can imagine, by doing that, we'll lose our data. But how can we handle these changes then? To handle these changes is possible using a single migration but in different steps. 1. Rename the column 2. Create the migration 3. Edit the migration 4. Execute the migration But let's see that in action. Imagine we want to rename the field firstName and lastName of the Author entity with givenName and familyName. The first action to do is rename these fields to our schema. Then update the `prisma/schema.prisma` in this way. ```prisma model Author { ... givenName String familyName String ... } ``` After that it's time to create our migration. This time though, we'll use a special option `--create-only`. This option allows us to create the migration script but this time the migration doesn't execute yet. Now execute the following command ```cli npx prisma migrate dev --name rename_author_columns --create-only ``` This command generates the new migration file but now we have to edit it to avoid data loss. If we open this file, we'll find it in this situation. ```sql PRAGMA foreign_keys=OFF; CREATE TABLE "new_authors" ( "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, "givenName" TEXT NOT NULL, "familyName" TEXT NOT NULL, "age" INTEGER NOT NULL ); INSERT INTO "new_authors" ("age", "id") SELECT "age", "id" FROM "authors"; DROP TABLE "authors"; ALTER TABLE "new_authors" RENAME TO "authors"; PRAGMA foreign_key_check; PRAGMA foreign_keys=ON; ``` As you can notice, all the data inside of the columns firstName and lastName will be lost. To prevent this loss we need to update the insert command. We can rewrite our insert in this way ```sql INSERT INTO "new_authors" ("id", "age", "givenName", "familyName") SELECT "id", "age", "firstName", "lastName" FROM "authors"; ``` Now it's time to do the last step to make this migration real ```cli npx prisma migrate dev ``` By running this command our migration will be executed and our database will be updated. As you can see, Prisma Migrate is a powerful tool that can help us but if we don't pay attention we could get hurt. If you are interested in going in-depth into migrations, I let you some links about it: - [Prisma Migrate](https://www.prisma.io/docs/concepts/components/prisma-migrate) - [Migration in Development](https://www.prisma.io/docs/concepts/components/prisma-migrate#development-environments) - [Migration in Production](https://www.prisma.io/docs/concepts/components/prisma-migrate#production-and-testing-environments) - [Customizing Migration](https://www.prisma.io/docs/guides/database/developing-with-prisma-migrate/customizing-migrations) Ok Guys, it's all for today! In the next article we are going to speak about seeding. See you soon Bye Bye πŸ‘‹ _The code of this article can be find [in the GitHub repository](https://github.com/Puppo/it-s-prisma-time/tree/15-migrations)_ --- ### Part 16 of 16: It's Prisma Time - Seeding - **URL**: https://delpuppo.net/blog/it-s-prisma-time/its-prisma-time-seeding/ - **Published**: Feb 4, 2022 - **Tags**: javascript, typescript, database, orm Hello Folks πŸ‘‹ Today we'll see how to handle seed with Prisma. Seeding allows you to re-create consistently the same data in your database and it can be used to: - Populate your database with data that is required for your application to start - for example, a default language or a default currency. - Provide basic data for validating and using your application in a development environment. This is particularly useful if you are using Prisma Migrate, which sometimes requires to reset your development database. Prisma exposes this feature out of the box. Creating a seed is not so difficult and now we'll see how to do it. The first step is to create a file that contains our seeds. In this case we are creating the file `index.ts` inside of the folder `prisma/seed`. Now, we want that someone executes this file every time. Its code adds inside of the authors table a special author called "Super Admin". The contents of this file therefore could be so ```ts import { PrismaClient } from "@prisma/client"; const run = async () => { const prisma = new PrismaClient(); try { if ((await prisma.author.count()) === 0) { await prisma.author.create({ data: { givenName: "Super", familyName: "Admin", age: 100, }, }); } else { console.log("Default author already created"); } } finally { await prisma.$disconnect(); } }; run(); ``` As you can notice, it's not so difficult to understand the code. We create a connection to the database and check if the author table is empty. If so, we add the "Super Admin" author. Now we have the file and it's time to execute it. Prisma is configurable to accept an instruction that can execute this code, and to do that we need to update our package.json file. The configuration is easy and we can instruct prisma in this way ```json ... "prisma": { "seed": "ts-node prisma/seed" }, ... ``` These three lines of code indicate to Prisma that we want to handle its seed command. When this command is executed the system uses the ts-node tool to build and execute the file `prisma/seed/index.ts`. But Prisma doesn't stop its work here. It executes this command after the migrate command too. Thus now we can execute this command ```cli npx prisma migrate dev ``` As you can see, Prisma executes the migration and our seed file. As a double check, we can update our `src/index.ts` in this way ```ts import { PrismaClient } from "@prisma/client"; async function main() { const prisma = new PrismaClient(); try { const authors = await prisma.author.findMany(); console.table(authors); } catch (error) { console.error(error); throw error; } finally { await prisma.$disconnect(); } } main(); ``` and after that run the command ```cli npm run dev ``` the result will seem like this ![Result](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0bntnxukvf6vkshm57te.png) Our database is ready with all migrations and our seeds. Now, I think you have all the notions to work with confidence with Prisma, so it's time to let you go for your steps. This is the last post of this [series](https://dev.to/this-is-learning/series/15827) folks. I hope my posts helped you to view better this fantastic tool, surely they helped me a lot to understand it better. So, I hope Prisma can be your friend in your daily work and it can help you to work in the right way with our databases. Before leaving you I wanted to thank the Prisma team for these fantastic swags. ![Swags](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/s4p3zx9d3e3dsafwxm5q.jpeg) And [This is Learning](https://twitter.com/Thisis_Learning) for letting me publish this content in its space. I know, I could do more for you but I promise that in the next future the time to do something live will arrive too (I apologize for my bad English spokenπŸ˜…). Thanks again This is Learning. Now it's really time to leave you. I hope you'll miss this Prisma time, but now it's your prisma time. Get those keyboards ready and let's code! Bye Bye Guys πŸ‘‹ See you soon with new contents! _The code of this article is published [in the GitHub repository](https://github.com/Puppo/it-s-prisma-time/tree/16-seed)_ ---