javascript
How to Get Started with Prisma ORM for Node.js and PostgreSQL
Last updated:
This post was updated on 9 August 2023 to use the latest stable version of Postgres v15 (from v13) and Prisma v5.1.1 (from v2.26.0).
An Object Relational Mapper (ORM) is a code library that plots the transfer of data stored in a database (usually relational) into objects represented in the code. Node.js works with some great ORM libraries like Sequelize, TypeORM, and Prisma.
In this post, we will learn how to use Prisma with the Express.js framework and PostgreSQL database. We will build a simple Quotes REST API to add and serve up programming-related quotes.
Let’s get started!
Introduction
As discussed, ORMs let us define our models as classes that map to tables in a database. Active Record and Data Mapper are two common patterns followed by ORMs. Both patterns require us to define classes to plot a connection to the database. However, there is a fundamental difference between them.
Active Record closely relates an application’s in-memory representation and database table. For instance, each field in the model will correspond to a field in the database table.
On the other hand, as the name suggests, Data Mapper loosely maps in-memory representation to a database table, decoupling the database and the model.
Prisma uses the Data Mapper pattern, similar to Doctrine ORM in the PHP world. With components like Prisma schema, client and migrate, it has the main bases covered.
Pros and Cons of Using an ORM
One of the most common pros of using an ORM is that you don't need to write SQL. Another advantage is that you can potentially switch databases without any issues, as vendor-specific logic is abstracted in an ORM.
The relation between rich, object-oriented business models is also defined well in the code. Without a doubt, the create, track, and rollback migrations are great reasons to vote for ORM usage.
On the flip side, using an ORM can cause performance issues. The N+1 problem is not hard to find in some ORMs. ORMs are made for general purposes, so they will add a layer of complexity to your application.
Another con of using an ORM is that the query builder can’t do some things that are pretty easy to do with raw SQL queries. Like many other things in software engineering, there will be a trade-off in using an ORM, so you have to choose wisely.
I have used multiple ORMs in my career. Once, I even had to rewrite an ORM-based implementation to raw SQL queries for a business because of some performance and data inconsistency issues. ORMs are an excellent tool for low to medium-level complexity and scale, but you might experience some problems at a higher load.
But this shouldn't stop you from wanting to use an ORM. Next up, we will look at some prerequisites for this tutorial, featuring Prisma, Express.js, and PostgreSQL.
Prerequisites
Before we dive into the code, here are some prerequisites:
- You should have Node.js running on your machine, ideally the LTS version.
- Knowledge and some experience of running npm commands is needed.
- A running local PostgreSQL database is required. You can run it with Docker if you wish.
- Basic knowledge of ORMs is beneficial but not necessary.
With those prerequisites out of our way, it's time to jump into some code.
Set Up Express.js
We'll begin by setting up Express.js for our programming languages Quotes API demo project. First, run the following to create a new project:
You'll get output similar to the following:
Next, install Express.js, our web framework for the Quotes REST API:
Hello World with Express
Next, we'll add an index.js
file with an API showing that Express is working. Create the file in the root of the project and add the following:
Let’s do a quick dissection of what the above code does. First, we initialize an Express app and declare a constant called port. If there is an environment variable called PORT
we use it — otherwise, it defaults to 3000.
We add a GET route on the root that responds with a simple JSON. Finally, we start the Express server and listen to the specified port with a message for requests. We run the server with:
Resulting in:
After that, if we hit http://localhost:3000
on a browser of our choice, it will show us something like the below:
The code I've shown you up to now is available in a pull request for your reference.
Next up, we'll set up a PostgreSQL database to create our tables for the Quotes API.
Set Up a Local PostgreSQL Database
There are multiple ways to run a PostgreSQL database on our local machine. If you already have one, you can move on to the next step. If you want to run a PostgreSQL database with Docker, use the following command:
It will give us this output:
The docker run
command creates a new docker container named postgres-quotes
, exposing the container port 5432 to the local port 5432. The --rm
parameter is added to remove the container when it stops.
As per the docs, we also set the password to be “mysecretpassword” for the default user postgres. Finally, in the docker run
command, we opt to use the 15.3-alpine image of postgres because it is smaller than the default one.
Our database is up and running. Next up, we will add Prisma to our Node.js project and create our schema.
Add Prisma ORM to Your Node.js Project
Execute the following command:
It will install Prisma as a dev dependency.
After that, initialize the Prisma schema with the following command:
On initializing the Prisma schema, we will get this output:
Consequently, we can add our local database connection string in the .env
file created. It will look as follows after the change:
The only change here is the DATABASE_URL
. For security reasons, it is best to pass the database URL as an environment variable in a production-like environment rather than put the credentials in a file.
Add Models and Run Prisma Migration
We will open the prisma.schema file
in the prisma folder and define our database tables for Quotes and Authors with their relation.
As one author can have multiple quotes and one quote will always have only one author, it will be defined as:
We have added two tables. The first one is the author table with id and name, and the name of the author is unique. The relation is that one author can have one or more quotes.
The following table is the quote table, which has an auto-increment ID and quote that is a unique string. It also has an author id to show which author said the quote.
To convert these models into PostgreSQL database tables, run the following command:
This generates the migration SQL that creates the tables and runs it against the specified database, resulting in this output:
If the migration is successful, it will have installed the Prisma client and added it to our package.json file. It will also create a migration.sql file that looks as shown below:
The above database tables match the model that we defined in the prisma.schema
file. The code in this part is available as a pull request here.
Below, you can see how the schema looks after importing the generated SQL to dbdiagram.io:
Next, we will seed the database with one author and a couple of quotes from that author.
How to Seed the PostgreSQL Database
To seed the PostgreSQL database with some initial data, we will create a seed.js
file in the same folder where we have our prisma.schema
file. Create the file and add the following to it:
The above seed file instantiates the Prisma client and then calls an Immediately Invoked Function Expression (IIEF) called main. In the main function, we upsert an author, Martin Fowler, with two amazing quotes.
If there is an error, it is logged, and the process exits. In the case of either success or error, we always disconnect from the database in the finally
part of the main function.
Prisma's integrated seeding functionality expects a command in the "seed"
key in the "prisma"
key of your package.json
file. Add the following to the file:
To seed the database with the data, run:
The above command will result in something like:
Read more about Prisma Migrate in the official docs. You can reference the seed changes in this pull request.
Hurray! We have one author and two related quotes from that author in the database. Now, we will expose these quotes in the form of JSON over a REST API.
API to View Quotes
We added quotes via a REST API endpoint with a GET call. We will change the index.js file we created in a previous step with a Hello world API to add the new GET Quotes API.
Make the following changes to index.js:
The main change here is that we instantiated the Prisma client. We also added the /quotes
GET API endpoint, which gets the quotes data with its authors using Prisma’s findMany method. We paginate the quotes with 5 per page — that's why we use skip
and take
in the findMany
parameters. There are other ways to paginate the rows with Prisma. We are opting for the offset-based approach in this example.
At this point, we can rerun the app with:
And if we hit http://localhost:3000/quotes
on a browser, we'll see output as shown below:
It may not be formatted as above, but the data is pulled from the table and served up as JSON with effectively 12 lines of code and no written SQL.
If we use AppSignal, we can also find the exact query and its performance on production. AppSignal has a magic dashboard for Node.js and PostgreSQL as well.
The code for GET Quotes API is available in this pull request.
Now we'll bring in a create Quotes API to add more quotes to our service.
Introduce a POST API to Add Quotes to PostgreSQL
To be able to add Quotes to the PostgreSQL database, we will introduce a POST API.
We'll add a route in the index.js file. First, add the following line after the express app is defined:
By adding the JSON middleware, express can now parse the JSON sent in the request body. We will add a route to handle additional Quotes as a POST quotes API endpoint, as follows:
The main logic here is to check if both the quote and author are in the request body. If that basic validation passes, we check if the author exists.
If the author does not exist, we create them and relate the quote to the author. If the author exists, we just create the quote and relate it to the existing author using the connect option in Prisma.
If there is an error on the server-side, we send back a 500 response code with a simple message and log the error for our reference. To test it out when the server is running, we will hit the API with the following curl:
It will come back to us with the following output:
As the author is not there, the quote creates them. If we try the following curl command:
It will not create the author, it will just add a quote and relate it to the existing author id 1 with the name Martin Fowler.
I hit the API with some more curl commands, and after adding the sixth quote, I tried http://localhost/quotes?page=2
to test out the pagination. It gave me only one quote — the sixth one I had added, as follows:
The code that adds the create Quotes API endpoint is accessible in this pull request.
I would strongly recommend you add the update and delete functionality. The Prisma docs can help you with that.
Please keep in mind that the validation done for this tutorial is super basic to keep things simple. In a real-life project, I recommend that you use a full-on validation library like Joi.
This brings us to wrapping up.
Wrap-up
We built a Quotes API with Express.js and Prisma ORM, using a PostgreSQL database running on Docker. I hope this gave you some new insights and was a good starting point to explore all of the software used in this tutorial.
Just keep in mind that an ORM is a great tool to reach for when your project is relatively small or mid-sized. In the case of lots of requests, the general nature of an ORM stands in the way of high performance.
As mentioned earlier, using an ORM often involves a trade-off — a balance between convenience and control. Be very careful with what you are leaving behind and make sure what you gain is worth it.
To finish up, I suggest you further explore some great Prisma ORM features. For instance, query optimization is a good one to read about.
Happy coding!
P.S. If you liked this post, subscribe to our new JavaScript Sorcery list for a monthly deep dive into more magical JavaScript tips and tricks.
P.P.S. If you need an APM for your Node.js app, go and check out the AppSignal APM for Node.js.