In this chapter we're going to focus on setting up our database and our ORM. We're going to start by installing Prisma into our project. We're then going to connect Prisma to a Postgres database using neon. Then we're going to explore Prisma Studio. And finally, we're going to explore and test Prisma API.
So where we left off last time was this click me button, which you can do now is you can actually shut down your app. So as you can see, I don't have any server running. And then once I refresh, I get this screen. So that's perfectly fine because we're just going to focus on installing packages for now. So using the link on the screen you can go ahead and visit this guide on how to use Prisma ORM with Next.js.
So the same as in the beginning as you can see prerequisites are Node.js 18 plus. So again I highly recommend that you are on a more up-to-date version of Node.js. So the step one here is to create the project. We can skip this because we already have a Next.js project. So no need to do this at all.
Instead, let's go ahead and let's actually just focus on installing and configuring Prisma. Let me zoom in a little bit for you here. And in here it's important that you change from Prisma Postgres into other databases so it's a subtle difference we are not going to need this. So switch to other databases here and let's do a dev dependency install of Prisma and TSX. So inside of my node-based project here I'm going to go ahead and install Prisma, TSX, both of them as dev dependencies.
Once they've been added I'm going to install Prisma Client in my normal dependencies. And Prisma and Prisma Client are pretty important packages for this project. So I would definitely suggest that you go ahead and immediately go inside of your project and just visit the package json. The reason I'm telling you this is so that you can see how far ahead your versions are. So I am on version 6.16.
You don't have to be on the exact version as me. Prisma can update quite frequently. Same thing for TSX, but I would recommend at least trying to be on the same major version as me. I highly doubt that there will be any breaking changes that soon in the next major version. Most of them are pretty backwards compatible, but then again, if you just want to install the exact same version as me, here is the version that I am using.
But I am very certain that you will be able to follow along with your version as well so no need to do anything special about that. Now that we have installed these packages let's actually initialize Prisma. So in here they give you the full command but we actually won't be needing these two at all. So let's just run npx prisma init. So I'm going to clear this, npx prisma init again inside of this project and let's just wait a few seconds and there we go.
So after this has been completed you can see that the Prisma schema was created at prisma forward slash schema dot prisma. So now when you go ahead inside of the Prisma folder you can find schema.prisma. Inside of here you can see the provider, you can see the output and you can see the default database provider is set to Postgres and the url is reading from our environment file and it's reading the database URL. This is something we don't yet have. So let me again confirm.
Yes, so only that file has been added. Schema.prisma and as you can see git ignore has been modified to ignore source generated Prisma because this is where the types for our database will be generated later on. So right now what's the problem is instead of dot environment here, you should have it because this Prisma init creates it, right? And you will have this very large comment inside. Now we're going to slightly modify this.
So I'm going to remove this entire comment here. And I'm going to set this to be just a simple comment database. And then I'm just going to go ahead and make this an empty string because now let's actually set up a scalable production ready database here so we can close this now and we can head to Neon using the link on the screen here. So just go ahead and create an account and once you're inside, let's go ahead and let's create a project. So I'm going to call this NodeBase.
These really don't matter. You can just hit create. And once you have a new project simply click connect to your database. And in here you can just go ahead and click copy snippet. So this entire snippet basically.
And once you have that that's going to be your database URL. As simple as that. Alright. So now let's go inside of Prisma, schema.prisma. And let me just tell you a little tip before we start writing anything here.
You've probably noticed that I have this clear syntax, right? I can very clearly see what's a value, that's a variable, and this specific syntax here. If you want the same, I highly recommend that you install the Prisma extension for Visual Studio Code. So that's how I get the syntax highlighting here. Right.
So that's pretty much it for the NEON database actually. It's super quick, it's scalable, and I love how good the developer experience is here. And you can also have a bunch of branches. It's basically perfect for use cases like this. Let's go ahead and let's see what we actually have to do next, right?
So we just set up Postgres database and we set up Prisma ORM. But in order to explore Prisma Studio and to test the Prisma API, we actually need to create some models, some tables for our database, right? And we actually do have this Prisma guide teaching us how to do that here. So again, you can use the link on the screen if you want to go here. So let's go ahead and just do that.
Let's add model user here and let's add model post. So I'm going to just zoom out a bit if you are not copying and pasting it's just these two simple models. So we have a user model which has a relation to many posts and we have a post model which has a relation to single or one user. Now let's go ahead and let's push this to our database. So the way we're going to do that is we're running npx prisma migrate dev and if you want to, yes, you can add the flag dash dash name but I'd rather we do it without just so you can see how it usually prompts you for the name so make sure you have added that in your schema prisma I mean, you don't really have to add this specifically you can add whatever you want really.
I just copied their example. So I'm now going to run npx prisma migrate dev. It's going to read my schema file and it's going to ask me after it connects of course to Neon DB is going to ask me what do I want to name this migration. So I'm going to follow their example and I'm just going to call this in it. And there we go.
We now have a new migration file here. Since this is development, these migration files aren't really too important, right? In production, obviously, this would be the way you would amend your schema. But in development, you can pretty much always just remove the migrations folder if you want to start over, if something has gotten complicated, if you feel like you've messed something up, you can always do that. So now that that works, What you can actually do is you can run Prisma Studio.
And Prisma Studio will open on localhost 5555. And you can see that now I have post and I have user models. And I'm pretty sure I can actually just add a record here. So for example, Antonio, mail.com, my name is Antonio. I will leave the posts empty and I didn't have to add this.
You can see a function will do this for me. So I just click save change And this should automatically add a new record here. There we go. And in order to confirm that this actually happened on the server in our database, head back to NEON. And inside of here they also have their own tables.
And there we go. Post nothing. User. We have one user with ID of 1, email Antonio and name Antonio. Exactly what we wanted.
So now let's go ahead and let's actually try and query those things. So what actually happened when we run npx prisma migrate to that? Well, besides creating a new migration and applying it, we also generated prisma client. What that did is, let me just try and find it. Generated folder, here it is.
So this folder right here now holds the types for our schema. So we can now safely access user.email and it will tell us that it's a required string. Or for example, user.name, it will tell us that that's an optional string. So that's what the generated folder is for and that's why it was added to gitignore here. So Let's go ahead first and let's establish the Prisma client.
So here's what we have to do. And I can't really remember where I found this exact guide, but I will explain to you why we need to do this. So let's go inside of the lib folder and let's create a new file called database.ts. Let's go ahead and import Prisma Client from generated Prisma. Let's define const global for Prisma to be global as unknown as open an object Prisma with a type of Prisma client.
So what this is doing is it's simply adding to the global object a new Prisma property and I'll explain why we are doing that in a second. Let's define the variable const Prisma to be global for Prisma dot Prisma or a new Prisma Client. Now let's check if process.environment.nodeEnvironment is not production, in that case global for Prisma.prisma is equal to the Prisma we just initialized. And let's export default Prisma. So what's going on here?
Well, technically we could be doing this. We could just import Prisma Client every time we need it. The problem is we would also have to do new Prisma Client every time. So, okay, why didn't I just do this and we don't need this and we don't need this and this way I would simply use this singleton instance. Well, that's because that wouldn't happen because of hot reload.
What's hot reload? Whenever your app is running, so if I do npm run dev, what's watching the changes in my file here? You can see when I, for example, add, if I add a little letter here and click save actually this is a bad example here let me just refresh localhost 3000 first basically hot reload is watching for changes right so If I go inside of my app, page.dsx, click Me Too, you can see that it immediately updates. So it immediately compiled. So that's exactly what's happening.
And the problem is, when hot reload happens, it will create new instances of Prisma Client multiple times and you will start to get warnings in your terminal about that. It will basically tell you multiple Prisma instances detected. As far as I'm aware, it's a development-only issue, but it can severely degrade the development speed when it comes to the dev server. It will become quite slow and maybe even some bugs will occur. So how do we fix that?
Well, we're using a little hack here. And I mean, when I say hack, this is an official solution. Right. I'm just really not sure which part of the documentation talks about this. But basically, one part of this environment, which is unaffected by hot reload is global.
So global is, well, a global variable, right? What we're doing here is we are storing the new Prisma client inside of that global.prisma property. What we did here is we simply created a special type that can understand what the Prisma is. If we didn't do this it will still work you would just get the type errors. I mean technically you would do it like this then right?
Global.prisma. So yes technically this works except it has no idea what Prisma is. So we just created a type for it right here because you can't put anything in global. So doing this ensures that no new Prisma client instances are created during hot reload. That's why we only do this in development mode.
There is no need to do this in production. In production, essentially what happens is this. This is what will happen in production. What you would expect to happen. But in development, we need to complicate it a little bit by adding it to the global object because global is unaffected by hop reload, so no new instances of Prisma Client are created.
Okay, I hope I managed to explain that. Great, now that we have that, let's go inside of source, app, page.tsx. So in Next.js, every page file by default is considered a server component. I can go in depth explaining server components but I think it's best to show you an example. In server components things like useEffect do not work.
So if I just go ahead and add a simple useEffect here and save, I will get an error. In order to fix it, I need to convert it to a client component by adding the useClient directive and that fixes it. But the cool thing about server components, while they can't accept useEffect, is that they can be asynchronous. And once they are asynchronous, They can do cool things like, for example, my users can be queried using a wait database, my apologies, Prisma from libdatabase.user.findmany. And then inside of here Let me go ahead and do json.stringify.users.
And once I save this, there we go. I successfully fetched the user from my database directly using Prisma in a server component. So that's the power of server components. It technically acts like an API route. Obviously, this isn't too safe.
So don't worry, we will add a proper data access layer using trpc and we are going to leverage both client components and server components because one thing that server components are good at is that they render, well I'm not sure if render is the correct terminology, but they appear before client components. So it would be wasteful not to use them to already start fetching something because it definitely will make the app faster. But we will talk about that in future chapters. For now, I just wanted us to achieve this very simple thing. I wanted us to add an item to our database and I wanted us to fetch it.
So, what if you want to remove all items from your database? Well, you can do that manually using Prisma Studio, but you can also do this which I think will be quite useful for you. You can run npx prisma migrate reset. Obviously, only do this in development. Don't even think about running something like this in production.
It will ask you to confirm, all data will be lost, confirm, and once you do that, well, all data is deleted. So there we go, immediately, if you refresh, you will see it's empty, perfect. So for now, I'm going to leave the Prisma schema as it is with this user model and the post model. Turns out we didn't need the post model at all. Sorry for making you write it.
But we will write our own models pretty soon and then I will explain in depth about this relations and this foreign keys. How does all of this work? How does the syntax work? I purposely didn't want to get into it right now. We just use it as a quick example to try and fetch something from our database.
So in the next chapter, we're going to focus, as I said, on the data access layer so that we can actually learn how we are going to fetch. Because while this is cool, it's not exactly the safest and it's not really scalable. So let's go ahead and see. Let's remove the unused button import while we are here and let's see inside of here what did we achieve. We definitely explored Prisma Studio and we tested the Prisma API.
So what I want to do now is I want to push to GitHub, but I want to do that by creating a new branch and creating a new pull request so we learn the actual Git workflow. And then let's review our pull request, so all the changes we did, and let's merge it. So I'm gonna go ahead like this instead of your source control you should have eight files so git ignore, package lock, package JSON, schema, two migration files, page and the database lib. So what I'm going to do now, go ahead and click on the main here, create new branch and I'm going to call this a 02 database And you can see that now I'm on my new branch. Now I'm going to stage all changes.
I'm going to name the commit the exact same, so 02 database, and I'm going to click commit and then I'm going to click publish branch. If for whatever reason you already clicked commit and you think oh how do I go back it's okay. So git workflows are optional to follow in this tutorial. I just think it's a good idea to give you an idea of how it would usually be done in branches. You obviously don't have to do it if you don't want to.
So if you just already committed, perfectly fine, no problem. So now that we have pushed this branch, Let's go ahead and open a pull request if you want to follow the git workflow and let's review it. So if you go to your nodebase repository you will now see 0-2 database had recent pushes But just in case you don't see this, you can always go manually inside of pull requests, click on new pull request, leave the base to be main and compare to be your new branch database. Go ahead and click create a pull request and again create a pull request. And now we're going to go over our files and review them.
And now you've probably noticed that I have some kind of summary here, which you probably don't. And that's completely Okay, that's expected. So let's first go over what I have here and then I'll show you exactly how you can have the exact same summary for every single one of your pull requests. So summary by CodeRabbit. New features.
Home page now dynamically loads and displays user data. Correct. Chores. We added database tooling and supporting dependencies. Prisma.
We initialized database schema with users and posts, including relationships as well as constraints. We added a migration lock configuration. And we converted the homepage component to an asynchronous component to support server-side data fetching. That's exactly what we did in this chapter and it's exactly what CodeRabbit summarized right here. So in here we can see a more in-depth walkthrough file by file.
So for example exactly what we did here and the summary. But what I really really love are these sequence diagrams. Right now we have a super simple situation in our app, so it's not too impressive. But nevertheless, you can imagine how useful this will be later on, especially when we introduce these complex, long-running background jobs and all of these different states that can happen here. So let's go through this sequence diagram generated by CodeRabbit.
So once the user visits the root page, what we do is we use the Prisma client, so we import the singleton file using from source lib database.ts. Using the Prisma client instance, we fire prisma.user.findMany, and we contact the Postgres database. The Postgres database then returns an array of users, and we finally render that in a form of JSON users on app page.tsx. So I absolutely love how CodeRabbit does all of this for us here. Obviously, this is a super simple pull request, so there's no need to review much now, but later on, we are bound to introduce some bugs in our projects, even some critical mistakes.
And you will be super impressed by how CodeRabbit will catch this and it will alert us before we merge that into our main branch. In my previous few projects, this has saved me countless times. So if you want the same thing, you can go ahead and use the link on the screen and simply create an account with CodeRabbit. You can get it completely for free. And if you don't like it in pull requests you can also install their extension.
So just go ahead and install the CodeRabbit extension here and connect the account which you've just created and you will get completely free AI code reviews for your branches and your pull requests inside of Visual Studio Code. Of course, all of this is completely optional. I just think it's extremely useful, but to each their own. Perfect. So I've just reviewed this pull request.
It's fairly simple. I reviewed the sequence diagram. It's exactly what we were expecting to happen. So let's go ahead and merge this pull request and let's confirm the merge. So I'm not going to delete my branch for a very simple reason, history.
So inside of my codebase here I like that I have a 02 database and I can always go back to it if I want to see the exact changes I did. So I've just merged a 02 database inside of my main branch which means that inside of my Visual Studio Code I also have to go back inside of my main branch. So I'm going to hit on 02 here and I'm going to select a main. You will have two main branches, this one and origin main. In our case it won't really matter which one you choose.
So one of these is remote and one of these is local. Since we're not doing anything complex, most of the time they will be exactly the same. But regardless, whichever one of these you choose, both are main. You can see main here, main here. Whichever one you choose, always make sure to click this little synchronize changes button and then click OK.
And this will push and pull all the changes from the GitHub repository. So you can see that now my main branch is completely in sync and it has the Prisma folder even though I developed that in a new branch. And in order to confirm you did this correctly, in your Visual Studio code you can go inside of source control and click on the graph here and you can see how I developed 0.1 setup on one level and then I detached into a new branch here for my 0.2 database and then I merged that back inside of my main branch here. So this is how it should look like if you followed my git workflow. Again, you can complete this entire tutorial without following the git workflow.
I just think it's useful to know but to each their own. You know why you're here, you know what you want to learn from this tutorial. Excellent! Amazing job! And now let's go ahead and just confirm we've done everything we wanted for this chapter.
Looks like the last thing here was to push to GitHub and that's exactly what we did. We created a new branch, we created a new pull request and we reviewed and merged our changes. Amazing, amazing job and see you in the next chapter where we're going to develop our data access layer using DRPC.