Building and maintaining applications typically require a structured way to handle database interactions. In Node.js applications, you can use an ORM (Object-Relational Mapping) tool such as Prisma to simplify database interactions as well as ensuring type safety. This will be built in NestJS, which is a progressive Node.js framework for building efficient, scalable, and enterprise-grade server-side applications with TypeScript/JavaScript.
In this article, I will walk you through the process of setting up Prisma in a NestJS project by creating a dedicated persistence module. This module will house a service that exports a Prisma client that will allow you to make database interactions more efficiently and consistently across your application.
If you would rather examine the code, I have gone ahead and uploaded it to Github. Feel free to take a look here
Prerequisites
Before diving into the implementation, make sure you have the following prerequisites:
- Node.js and npm: Make sure you have Node.js (version 16 or higher) and npm installed on your machine. You can download them from nodejs.org.
- NestJS CLI: The NestJS CLI helps in quickly scaffolding and managing NestJS projects. You can install it globally using npm:
- Postgres Database: I will be using Postgres for my database. You can use local installation or a cloud service like Neon.
- Basic Knowledge of TypeScript and NestJS: Familiarity with TypeScript and NestJS will help you follow along.
- Prisma CLI: Prisma CLI is necessary for initializing and managing Prisma in your project. Install it globally using npm:
Once these prerequisites are in place, you're all set to start building a persistence module in your NestJS application using Prisma.
Setting Up the Project
If you are starting from scratch, create a new NestJS project using the Nest CLI:
nest new project-name
Next, install the required dependencies:
npm install prisma @prisma/client
Setting up Prisma
Before building out our functionality, lets set up our connection to our database. To do so, we will need to initialize Prisma by using the following command:
npx prisma init
This will create a Prisma directory in your project that includes a prisma.schema
file. This file is used to configure the connection to your database as well as the schema used to define the tables.
You will also notice that a .env
file has been created in the root directory of your project. This is where secure variables such as connection information to your database is stored. If you have not worked with .env
files before, these should be kept private and not shared on Github or anywhere else.
Let's first add our database connection information to the .env
file so it may look like the following:
If you are using Neon, the connection string can be grabbed from the Dashboard > Connection Details section
Next, let's define the tables we will be using in the prisma.schema
file.
If we take a look at the schema, we can see that first we are using the DATABASE_URL
from the .env
file to set up our connection to database.
Next, we are creating a model named User. The User model contains information about the user such as a unique id, email, display name, and password.
With our schema made, we can now migrate it to our database by using the following command:
npx prisma migrate dev --name init
After completing the prompt in the terminal, this command will do two things:
- It will create a new SQL migration file for this migration
- It will run the SQL migration file against the database
Creating the Persistence Module
Now that we have created a connection to our database and initialized it, we can build a client to interact with it.
In NestJS, modules are used to organize and encapsulate related functionality. Let's create a persistence
module we can use to interact with our Prisma client.
To create a module, you can either manually create the directory and the scripts inside it or you can use NestJS's CLI tool to generate the modules for you. To generate the persistence
module, you can run the following command:
For additional information on the
nest generate
tool, you can runnest generate --help
This will create a directory named persistence
and contain a new module script persistence.module.ts
which will can be used to export services in the module. Next, we will create the persistence service.
This will create 2 scripts:
persistence.service.ts
- used to create the servicepersistence.service.spec.ts
- used for creating unit tests for the service I will not be covering unit testing in this article as I believe it needs its own article to cover so we will be ignoring the spec files for now.
In the persistence.service.ts
use the following to define the Prisma service:
As Prisma follows a singleton pattern, this script allows for a single Prisma instance to be initialized and will create a client for us to interact with.
Now that the service is made, we can update the module to export the PersistenceService
so we can use it across the rest of our application.
Using the Persistence Service in Other Parts of the Application
With the PersistenceService
available, let's demonstrate how to use it in a different module. For this example, let's assume we have a UserModule
where we want to interact with the database to manage user data.
Creating a User Module
First, generate a UserModule
, UserService
, and UserController
using the NestJS CLI:
This will create the necessary files for the UserModule
, UserService
, and UserController
. Next, we'll update the UserService
to use the PersistenceService
.
Injecting PersistenceService
Open the user.service.ts
file and update it to inject the PersistenceService
in the constructor:
In this example, the UserService
class is using the PersistenceService
to interact with the newly create User
table in the database. The createUser
method creates a new user, and the getUserByEmail
method retrieves a user by their email address.
In my example project, there is also a utility used to hash the users passwords. When storing sensitive data such as passwords, it is super important to securely store the data by encrypting it.
Implementing the UserService
Now that we have a UserService
we can call it's functions in the UserController
. We can update the user.controller.ts
script to the following:
In the above controller, we have created 2 new endpoints for our application:
- GET
/user/:id
- finds a user using the provided ID - POST
/user
- creates a new user with data from the JSON body
Finishing Up
Next, we make sure the UserService
& UserController
has been to the UserModule
and that we are importing our PersistenceModule
. Open the user.module.ts
file and update it as follows:
By importing the PersistenceModule
, the UserModule
can use the PersistenceService
provided by it.
Testing out the service
We can now spin up our test environment by running the following command in the root directory of our project:
This will start our API locally where we can test our newly created endpoints. As we can see, they can create a user and return a user:
Create User
Get User
Please note that in a production app these would require further validation and authentication.
Wrapping Up
In this article, we have covered how to set up Prisma in a NestJS project and build a persistence module. We also demonstrated how to integrate this module into other services. By following these steps, you can manage database interactions in your NestJS applications.
Summary of Steps:
- Setting up Prisma:
- Initialize Prisma with
npx prisma init
. - Configure the database connection in
.env
. - Define database schema in
prisma.schema
. - Migrate the schema with
npx prisma migrate dev --name init
.
- Initialize Prisma with
- Creating the Persistence Module:
- Generate the
persistence
module and service. - Implement the
PersistenceService
to manage Prisma client connections. - Export the
PersistenceService
from thePersistenceModule
.
- Generate the
- Integrating and Using the Persistence Module:
- Import
PersistenceModule
into the mainAppModule
. - Create other modules (e.g.,
UserModule
) and services (e.g.,UserService
) that use thePersistenceService
. With this setup, you now have a foundation for handling database interactions in your NestJS application. Happy coding and I hope this helps!
- Import