In this post, we will learn how to use Prisma, an ORM for JavaScript and TypeScript, in a Next.js app.
What is Prisma?
Prisma is an open-source ORM tool for Node.js and TypeScript, that simplifies connection, querying, migrations, and data modeling to SQL databases.
ORM is Object-Relational Mapping, it is a technique in which we can query data, connect and manipulate databases using the object-oriented paradigm. ORMs can be written in any language, in whatever language it is written in, it encapsulates the code required to manipulate the database. This eliminates the use of SQL because that is being taken care of by the ORM library. The ORM represents each table in the data schema as a single class. For example, a Food table will turn to a class in an ORM library and we can read all food items, delete foods, etc using this class.
To query for all foods in the database manually, we will write the code:
So much code. But with an ORM library, it will be this:
Simple and neat. With Prisma, it is simpler:
Using Prisma is very simple. The team at Prisma provides us with a CLI tool that makes generating Prisma client code a breeze. To get started with Prisma, we define the schema of our database in a schema.prisma file:
Prisma will run migrations of these against our database. We will now use Prisma client library to query the database.
The findMany, create, and update methods run SQL queries against our database. But as we can see we don't have to write SQL commands and queries, Prisma takes care of that for us. We are left with a cleaner code that is easier to maintain. The mechanical part is automatically taken care of for us by Prisma
That's lit! ๐ฅ
Prisma and Next.js
Prisma integrates well with Next.js. Next.js combines both frontend code and backend code. This means that we can build the UI components in Next.js and also build our API routes in the same Next.js app, so Next.js contains the client and server. Prisma's being a connection to a database makes it ideal for us to call it in our Next.js app to fetch data from the backend for us. There are many ways in Next.js where we can fetch data. Next.js has three unique functions we can use to fetch data for pre-rendering:
getStaticProps (Static Generation): Fetch data at build time. We will use the Prisma client to perform queries to our DB.
Next.js will perform the query during build time to fetch the data, then we return the results in the props object, this will make Next.js pass the results to the props of the corresponding component.
getServerSideProps (Server-side Rendering): Runs when the page is being pre-rendered on each request. We can also call the Prisma client methods here to fetch data that we want to pass to the Next.js components.
API routes: API routes in Next.js are kept in the pages/api folder. Each file and folder maps to an API endpoint. They are served under the same URL path as the frontend code, localhost:3000. So as localhost:3000/foods renders the food page, so also localhost:3000/api/getAllFoods is an API endpoint that returns lists of food recipes from the Next.js app.
We can make calls to our database via Prisma from here. For e.g localhost:3000/api/getAllFoods endpoint can query the database to retrieve all the food recipes and send it as a response:
See that we are using the Prisma from Node.js; the API routes, getServerSideProps and getStaticProps. All those are run on the Next.js server, we can't use Prisma from the browser:
doing that will give an error:
To fully demonstrate how to use Prisma and Next.js, we will build a Food app in Next.js. The Food app will be able to:
- list all foods
- get a particular
- Display a food; its ingredients, description, and price.
- Delete a food
- Edit a food recipe.
All these actions will be performed by Prisma from the Next.js app. The foods will be stored in a Postgres database, and we will use the Prisma client to get all the foods and modify them as we listed above. Our final app will look like this:
Our app will have two pages, one to display all the food recipes and the other to display a specific food recipe. So our Next.js app will have two components for the two views/routes/pages. The page routes will be:
- /foods: Displays all the food recipes in our app.
- /food: Display a specific food recipe.
We will have presentational components that will display little detail of each recipe on the foods list page, and a header component to display the header. We will have two modals/dialogs where we can add new food recipes and edit an existing food recipe. We will have API routes that will expose three endpoints: one to add a new food recipe to the db, the other to edit a specific food recipe, and the last to delete a food recipe from the db.
Letโs roll.
Setup Postgres in your machine
We will be using Postgres as our database. Prisma connects to any SQL database: Postgres, MySQL, SQL Server, SQLite. It also has connections to MongoDB. If you don't have PostgresSQL installed in your machine head over to PostgresSQL downloads and download the binaries for your machine.
After installation, start the Postgres server. Make sure you remember the Postgres port, username, password because we will use them in connecting the Prisma client to the Postgres.
Scaffold a Next.js project
Now, we will scaffold a Next.js app, run the command to do so:
Move inside the folder: cd prisma-next. We will install the following dependencies:
- axios: HTTP library for performing HTTP requests.
- @prisma/client: Prisma client for JavaScript. It only runs on Node.js.
Run the below command:
We are done, next, we initialize a Prisma environment in our project.
Prisma init
Run the below command:
This command will create a prisma folder inside prisma-next project and a .env file. The prisma folder will contain a schema.prisma file, this is where we declare our Prisma database models. Next, we set up the Prisma connections to our Postgres DB.
Setup Prisma connection to the database
Open the .env, we will see it has a DATABASE_URL:
This is the URL connection to a Postgres database server.
- johndoe is the name of the database user.
- randompassword is the password for the database user.
- localhost is the host of the database.
- 5432 is the port number, it is always 5432 by default.
- mydb is the name of the database you want to connect to. Create this in your Postgres server.
Now change them to your own Postgres details. Mine is:
Define our model
Now, we will define our model. Since our app will be on food recipes. Our food model will be this:
The id is set by the database and auto-incremented. The name will be the name of the food recipe. The price will be the price of the food recipe. The ingredients will contain the ingredients used to prepare the food. The active indicates whether this food is currently available. The description describes the food recipe. The imageUrl is the URL to the picture of the food recipe. The Food model will map to a table in our database.
Run migrations
Next, we run migrations. Running migrations, will create an SQL migration file for the current schema, and run the migrations against the database. Migrations run whenever we update the schema. Migrations are just SQL commands that are generated based on what was performed on the schema. If we create a new Model in the schema, the migrations will create an SQL command to create a table, eg:
This SQL command will then be executed to create the table in the database. That's the summary of running migrations. So whenever you hear of migrations, bear in mind that this is what is being done. OK, so back to our project. Now, we run the below command to run migrations on our Postgres database:
This command will generate the migrations file and run them against your db. The --name sub-arg sets the name of the migration. The value which is init will be the name of the migrations folder generated. The output is: {NUMBER_GENERATED_BY_PRISMA}_init. So this will generate a folder/file inside the prisma/migrations folder. A new folder with a migration.sql SQL file will be generated for each migration run. In my machine the command generated this:
The migration.sql contains SQL command to create the Food table:
This is because we added a model to the schema. So Prisma generated the command from the schema and ran it. OK, let's build our components.
Build the components
Like we said, our app will have two pages: foods and food. Let's create them:
food is a dynamic route, that's why we used [id].js. The square bracket syntax is how to tell Next.js that a route is a dynamic route. The route can map to anything e.g food/1, food/2, food/1987, etc. We will create the presentational components: header and FoodCard:
We create the modal components: editfood and addfood:
The *.module.css is CSS Modules way to style each component separately Now we flesh out the components:
FoodCard
Open the components/foodcard/index.js and paste the below code:
This FoodCard component renders the details of a food recipe, just the name, image, price, and availability of the food. It accepts the food recipe from its props in the props.food.
Header
Open the components/header/index.js and paste the below code:
This component renders the header.
Foods
Open the pages/foods/index.js and paste the below code:
This component fetches the food recipes and renders them in a list. See that it initializes the Prism client:
And the foods are gotten in the getServerSideProps method.
We call the prisma.food.findMany() method to get all the foods on the food table. See that the models in the prisma.schema are available in prisma object. We have Food model, so that's why we have food in the prisma object. Now, each model available in the prisma object contains methods to get all the model data, modify the model data and delete the model data: findMany, update, upsert, delete, find, etc.
So we set a foods key in the props to hold the food recipes result in the allFoods so this way Foods can access the food recipes returned by doing this props.foods. Looking inside the Foods component we have a showAddFoodModal state that we use to toggle the AddFood modal. Next, the food recipes are retrieved from the props and stored in the foods variable. Then, we render the food recipes in the foods array, each food in the array is rendered by the FoodCard. Each food in the array is passed to it via the food prop.
Food
Open pages/food/index.js and paste the below code:
This page will get the id of a food recipe, use the id to fetch the food recipe, and display all its details. See in the getServerSideProps method:
We retrieved the value of the id of the route from the context.params. Now we called the prisma.food.findUnique method to fetch the food recipe whose id is the value of the id we retrieved from the context.params. The result is passed to the props object in a food key.
The Food component, retrieves the food result from its props arg and uses it to render all the content of the food recipe. There are Edit and Delete buttons. The Delete button when clicked clears out the food from the db. It calls the api/deleteFood in our app, the endpoint does the work and returns the response and we move to the foodslist since the food recipe we are viewing no longer exists. The Edit brings up the EditFood modal for us to edit the current food we are viewing. This is done by the showEditFoodModal state, it toggles the modal on or off.
EditFood
Open the components/editfood/index.js and paste the below code:
This is modal component that we can edit a food recipe passed to it. The food to edit is passed via its props arg in the food key. Also, closeModal function is passed too, this is called from the component to toggle the modal's visibility. The component has input boxes that render the values of the food's info, we can edit them there and press the Save button. This calls the editFood method, which retrieves the values from the input boxes and calls the /api/editFood API endpoint with the data. This API endpoint is responsible for updating food info on the db.
AddFood
Open the compoennt/addfood/index.js file and paste the below code:
Here, we create a new food recipe. The input boxes are where we add the info for a new food recipe. The Add button when clicked will call the addNewFood function. This function will retrieve the new food data from the input boxes and call the api/addFood API endpoint with this info. The endpoint is responsible for adding new food recipe to our database. After the API endpoint call, the page is reloaded so we see the newly created food recipe. Now we are done with our components let's create the API endpoints.
Creating the API routes
Create the following files:
/api/addFood
The api/addFood.js will create a new food recipe in our database. Paste the below code in it:
The data of the food recipe is retrieved from the req argument in its .body object. According to the Next.js docs:
req: An instance of http.IncomingMessage, plus some pre-built middlewares you can see here
res: An instance of http.ServerResponse, plus some helper functions you can see here
So back to our addFood.js code, we call the prisma.food.create method passing in the data to create the new food recipe in the database. Then, the result is returned as a response with a status code of 200.
editFood
Open the pages/api/editFood.js and paste the below code:
The new info of the food recipe is extracted from the req.body object. Next, the prisma.food.update method is called to update the food recipe whose id is equal to the id extracted from the req.body with the new info.
deleteFood
Open the pages/api/deleteFood.js, and paste the below code:
This extracts the id of the food recipe to delete. Next, we deleted the food recipe by calling prisma.food.delete method by the id of the food recipe. The prisma.food.delete method deletes a single food recipe from the Food record in the Postgres database. The where object is used to tell the Prisma the condition in which the Prisma will select a record from the table to delete. Now, let's test our app.
Testing
Run:
and open your browser to localhost:3000.
Adding a new food:
Viewing a food:
Editing a food:
Deleting a food:
Conclusion
Prisma is mind-blowing. The combination with Next.js will be the best combo of the century. It was flawless using an SQL database in Next.js. All the mechanical parts of using SQL have been abstracted away by Prisma. That's perfect!๐ฅ
Source code
Find the source code of the project here