DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Related

  • Translating OData Queries to MongoDB in Java With Jamolingo
  • GraphQL vs REST — Which Is Better?
  • Level Up Your API Design: 8 Principles for World-Class REST APIs
  • GraphQL vs REST API: Which Is Better for Your Project in 2025?

Trending

  • Contract-First Integration: Building Scalable Systems With Flyway, OpenAPI, and Kafka
  • Your AI Agent Tests Are Passing, But Your Agent Is Still Broken
  • Building a DevOps-Ready Internal Developer Platform: A Hands-On Guide to Golden Paths, Self-Service, and Automated Delivery Pipelines
  • Feature Flag Debt: Performance Impact in Enterprise Applications
  1. DZone
  2. Software Design and Architecture
  3. Integration
  4. Building a NestJS Rest API Using Prisma ORM

Building a NestJS Rest API Using Prisma ORM

Learn how to build a NestJS REST API using Prisma ORM.

By 
Saurabh Dashora user avatar
Saurabh Dashora
DZone Core CORE ·
Oct. 20, 22 · Tutorial
Likes (2)
Comment
Save
Tweet
Share
4.8K Views

Join the DZone community and get the full member experience.

Join For Free

NestJS is a pretty solid framework for building backend applications. 

In this post, we are going to take our NestJS knowledge and combine it with Prisma to build a RESTful API. This post is going to have a bunch of code that you can get from the Github link at the end or you can also code along.

In case you are new to NestJS, you can start with this post for starting with NestJS.

What Is Prisma?

Prisma is a next-generation Object Relational Mapper (ORM). We can use it with Typescript as well as Javascript. It takes a somewhat different approach to traditional ORMs. You can check out Prisma’s official website for more information.

Instead of classes, Prisma uses a special Schema Definition Language.

Basically, developers describe their schemas using this Schema Definition Language. Prisma runs over the schemas and writes the appropriate migrations depending on the chosen database. It also generates type-safe code to interact with the database.

In other words, Prisma provides an alternative to writing plain SQL queries or using other ORMs (such as TypeORM or Sequelize). It can work with various databases such as PostgreSQL, MySQL, SQLite and even MongoDB.

Prisma consists of two main parts:

  • Prisma Migrate – This is the migration tool provided by Prisma. It helps us keep our database schema in sync with the Prisma schema. For every change to our schema, Prisma Migrate generates a migration file. In this way, it also helps maintain a history of all changes that may happen to our schema.
  • Prisma Client – This is the auto-generated query builder. The Prisma Client acts as the bridge between our application code and the database. It also provides type safety.

We will utilize both Prisma Migrate and Prisma Client to build our application.

How Good Is Prisma ORM?

While this might be a subjective question, Prisma aims to make it easy for developers to deal with database queries.

As any developer will know, it is absolutely necessary for most applications to interact with databases to manage data. This interaction can occur using raw queries or ORM frameworks (such as TypeORM). While raw queries or query builders provide more control, they reduce the overall developer productivity.

On the other hand, ORM frameworks abstract the SQL by defining the database model as classes. This increases productivity but drastically reduces developer control. It also leads to object-impedance mismatch.

Object Impedance Mismatch is a conceptual problem that arises when an object oriented programming language interacts with a relational database. In a relational database, data is normalized and links between different entities is via foreign keys. However, objects establish the same relation using nested structures. Developers writing application code are used to thinking about objects and their structure. This causes a mismatch when dealing with a relational database.

Prisma attempts to solve the issues around object-relational mapping by making developers more productive while giving them more control.

Some important ways how Prisma achieves this are as follows:

  • It allows developers to think in terms of objects.
  • It helps avoid complex model objects
  • It helps maintain a single source of truth for both database and application using schema files
  • Facilitates writing type-safe queries to catch errors during compile time
  • Less boilerplate code. Developers can simply define their schemas and not worry about specific ORM frameworks.

Setting up a NestJS Prisma Project

With a basic understanding of Prisma, we can now start to build our NestJS Prisma REST API.

As the first step, we create a new NestJS Project using the Nest CLI. The below command creates a new working NestJS project.

Shell
 
$ nest new nestjs-prisma-demo-app
$ cd nestjs-prisma-demo-app


In the next step, we install Prisma.

Shell
 
$ npm install prisma --save-dev


Note that this is only a development dependency.

We will be using the Prisma CLI to work with Prisma. To invoke the CLI, we use npx as below.

Shell
 
$ npx prisma


Once Prisma is activated, we create our initial Prisma setup.

Shell
 
$ npx prisma init


This command creates a new directory named prisma and a configuration file within our project directory.

  • schema.prisma – This is the most important file from Prisma’s perspective. It will be present within the prisma directory. It specifies the database connection and also contains the database schema. You could think of it as the core of our application.
  • .env – This is like an environment configuration file. It stores the database hostname and credentials. Take care not to commit this file to the source repository if it contains database credentials.

Prisma Database Connection Setup

For our demo NestJS Prisma application, we will be using SQLite as our database. This is an easy-to-use database option since SQLite stores data in files. As a result, we don’t have to set up database servers.

To configure our database connection, we have to make changes in the schema.prisma file.

JavaScript
 
generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "sqlite"
  url      = env("DATABASE_URL")
}


Basically, we have to change the provider in datasource DB section to sqlite. By default, it uses Postgres, but you can also use other database solutions.

The second change is in the .env file.

JavaScript
 
DATABASE_URL="file:./test.db"


Here, we specify the path to our database file. The file name is test.db. You can name it anything you want.

Prisma Migrate Command

Now, we are ready to create tables in our SQLite database.

To do so, we will first write our schema. As discussed earlier, the schema is defined in the schema.prisma file we saw a moment ago.

We will update the same file as below:

JavaScript
 
generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "sqlite"
  url      = env("DATABASE_URL")
}

model Book {
  id  Int @default(autoincrement()) @id
  title String
  author String
  publishYear Int
}


As you can see, we have used the schema definition language to create a model named Book. It has a few basic attributes such as title, author and the publishYear. The id is an integer and is set to auto-increment.

This schema is the single source of truth for our application and also the database. No need to write any other classes as we have to do for other ORMs such as TypeORM. More on that in a future post.

We will now generate the migration files using Prisma Migrate.

Shell
 
$ npx prisma migrate dev --name init


Basically, this command generates SQL files and also runs them on the configured database. You should be able to find the below SQL file within the prisma/migrations directory in your project.

SQL
 
CREATE TABLE "Book" (
    "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
    "title" TEXT NOT NULL,
    "author" TEXT NOT NULL,
    "publishYear" INTEGER NOT NULL
);


Also, the database file test.db will be automatically created.

Installing and Generating Prisma Client

Our database is now ready. However, we still don’t have a way to interact with the database from our application.

This is where the Prisma Client comes into the picture.

But what is Prisma Client?

Prisma Client is a type-safe database client to interact with our database. It is generated using the model definition from our prisma.schema file. In other words, the client exposes CRUD operations specific to our model.

We can install Prisma Client using the below command.

Shell
 
$ npm install @prisma/client


Under the hood, the installation step also executes the prisma generate command. In case we make changes to our schema (like adding a field or a new model), we can simply invoke prisma generate command to update our Prisma Client accordingly.

Once the installation is successful, the Prisma Client library in node_modules/@prisma/client is updated accordingly.

Creating the Database Service

It is not good practice to directly work with the Prisma Client API in our core application. Therefore, we will abstract away the Prisma Client API within another service.

See below code from the db.service.ts file.

TypeScript
 
import { INestApplication, Injectable, OnModuleInit } from "@nestjs/common";
import { PrismaClient } from "@prisma/client";

@Injectable()
export class DBService extends PrismaClient implements OnModuleInit {

    async onModuleInit() {
        await this.$connect();
    }

    async enableShutdownHooks(app: INestApplication) {
        this.$on('beforeExit', async () => {
            await app.close();
        })
    }
}


Basically, this is our application’s database service that extends the PrismaClient we generated in the previous step. It implements the interface OnModuleInit. If we don’t use OnModuleInit, Prisma connects to the database lazily.

Prisma also has its own shutdown mechanism where it destroys the database connection. Therefore, we implement the enableShutdownHooks() method in the DBService and also call it in the main.ts file.

TypeScript
 
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { DBService } from './db.service';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  const dbService: DBService = app.get(DBService);
  dbService.enableShutdownHooks(app)
  await app.listen(3000);
}
bootstrap();


Creating the Application Service

Now that our database service is set up, we can create the actual application service. This service exposes methods to read, create, update and delete a book from the database.

See the below code from the file named book.service.ts:

TypeScript
 
import { Injectable } from "@nestjs/common";
import { Book, Prisma } from "@prisma/client";
import { DBService } from "./db.service";

@Injectable()
export class BookService {

    constructor(private dbService: DBService) {}

    async getBook(id: Prisma.BookWhereUniqueInput): Promise<Book | null> {
        return this.dbService.book.findUnique({
            where: id
        })
    }

    async createBook(data: Prisma.BookCreateInput): Promise<Book> {
        return this.dbService.book.create({
            data,
        })
    }

    async updateBook(params: {
        where: Prisma.BookWhereUniqueInput;
        data: Prisma.BookUpdateInput;
    }): Promise<Book> {
        const { where, data } = params;
        return this.dbService.book.update({
            data,
            where,
        });
    }

    async deleteBook(where: Prisma.BookWhereUniqueInput): Promise<Book> {
        return this.dbService.book.delete({
            where,
        });
    }
}


This is a standard NestJS Service where we inject an instance of the DBService. However, an important point to note is the use of Prisma Client’s generated types such as BookCreateInput, BookUpdateInput, Book etc to ensure that the methods of our service are properly typed. There is no need to create any additional DTOs or interfaces to support type safety.

Creating the REST API Controller

Finally, we can create a NestJS Controller to implement the REST API endpoints.

See the below code from the file named book.controller.ts.

TypeScript
 
import { Body, Controller, Delete, Get, Param, Post, Put } from "@nestjs/common";
import { Book } from "@prisma/client";
import { BookService } from "./book.service";

@Controller()
export class BookController {
    constructor(
        private readonly bookService: BookService
    ) {}

    @Get('books/:id')
    async getBookById(@Param('id') id: string): Promise<Book> {
        return this.bookService.getBook({id: Number(id)});
    }

    @Post('books')
    async createBook(@Body() bookData: {title: string, author: string, publishYear: Number}): Promise<Book> {
        const { title, author } = bookData;
        const publishYear = Number(bookData.publishYear);
        return this.bookService.createBook({
            title,
            author,
            publishYear
        })
    }   

    @Put('books/:id')
    async updateBook(@Param('id') id: string, @Body() bookData: {title: string, author: string, publishYear: Number}): Promise<Book> {
        const { title, author } = bookData;
        const publishYear = Number(bookData.publishYear);

        return this.bookService.updateBook({
            where: {id: Number(id)},
            data: {
                title, 
                author,
                publishYear
            }
        })
    }

    @Delete('books/:id') 
    async deleteBook(@Param('id') id: string): Promise<Book> {
        return this.bookService.deleteBook({
            id: Number(id)
        })
    }
}


Here also, we use the Book type generated by the Prisma Client to implement type-safety.

As the last step, we configure the controllers and services in the App Module (app.module.ts file) so that NestJS can discover them during application startup.

TypeScript
 
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { BookController } from './book.controller';
import { BookService } from './book.service';
import { DBService } from './db.service';

@Module({
  imports: [],
  controllers: [AppController, BookController],
  providers: [AppService, BookService, DBService],
})
export class AppModule {}


Our application is now ready. We can start the application using npm run start and test the various endpoints at http://localhost:3000/books.

With this, we have successfully created a NestJS REST API using Prisma ORM. We started from the very basics of Prisma and worked our way to writing the schema, generating a migration and a client to interact with our database tables.

The code for this post is available on GitHub in case you want to study it more closely.

How did you find this post? Have you already started using Prisma ORM in your projects? If yes, how do you find it in terms of usability? Or are you using other ORM frameworks in your projects?

I would love to hear your thoughts on this matter. So, please post your views in the comments section. And if this post was helpful, please like and share it. This will help the post reach more people.

API REST

Published at DZone with permission of Saurabh Dashora. See the original article here.

Opinions expressed by DZone contributors are their own.

Related

  • Translating OData Queries to MongoDB in Java With Jamolingo
  • GraphQL vs REST — Which Is Better?
  • Level Up Your API Design: 8 Principles for World-Class REST APIs
  • GraphQL vs REST API: Which Is Better for Your Project in 2025?

Partner Resources

×

Comments

The likes didn't load as expected. Please refresh the page and try again.

  • RSS
  • X
  • Facebook

ABOUT US

  • About DZone
  • Support and feedback
  • Community research

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 215
  • Nashville, TN 37211
  • [email protected]

Let's be friends:

  • RSS
  • X
  • Facebook