NestJs x Prisma: Made for each other
Tags: node, typescript, prisma, nestjs
Published: 2022-09-10 12:37:06

**TL/DR:** Find the full code on github [here](https://github.com/majiyd/nest-prisma-basic-tutorial)

I started using [NestJs](https://nestjs.com/) and [Prisma](https://www.prisma.io/) in 2021 and my life has never remained the same. When combined with Typescript, these tools would rapidly cut down development time.

NestJs is a progressive Node.js framework for building efficient, reliable and scalable server-side applications while Prisma is a next-generation Node.js and TypeScript ORM. 

For those unfamiliar, an ORM or object-relational mapper (ORM) is an abstration layer that allows you to write sql queries in the programming language you're comfortable with it instead of SQL. It automates the transfer of data stored in relational database tables into objects that are more commonly used in application code

For example:

```sql
SELECT * FROM USERS WHERE zip_code=94107;
```
would be written in with prisma as

```js
const users = await prismaService.findMany({
  where: {
    zip_code: 94107
  }
})
```

One thing you'd definitely love is the automatic type support and auto-suggest. These guarantee that you can never make invalid queries and also help you write queries faster.

Enough talk! Let's get into it. In this tutorial, we'll be creating a small REST api (GraphQL works too!) with a bunch of users and these users can put up items for rent.

## Setting up NestJs
First, we setup nestjs by running

```bash
yarn global add @nestjs/cli @nestjs/schematics
nest new rently # our new nestjs app
```
This would bootstrap a NestJs app. You'd also find some files created. These are the root module, contoller and test files. You can find out more about them [here](https://docs.nestjs.com/first-steps) but for now, I don't think you should worry too much about it.

![Nest-js-default-files](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/k9ec3f5wkusx7aequa62.png)

Let's run the application in dev mode with 

```bash
yarn start:dev
```
Our app is running fine!

![nest-app-running](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zxh1474d1fdz9o24j59t.png)

Now, let's head to postman and send a get request to `localhost:3000` and we should see this!


![hello world from nest](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/k1hijyz6lqlk3pf9q0d4.png)

Let's go back to the app module and talk a little about the files generated. In summary, the controller picks up the http requests

![app-controller](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/053yvlyddgftyeyv1nki.png)

while the service files handles the actual logic e.g database queries, computations, communicating with external services, etc.


![app-service](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/umbrd29gynmgjkzpej7c.png)

Think of the module file as the point of entry or the registration point for external modules used within the service.

Exciting!

Now we're going to create our first module called users. With this module we should be able to create a new user, get a single user and get all users.

You can create the files for the user module with `nest g service users && nest g controller users && nest g module users`

You should see something like this
![user-module](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vlovbog0ijtdwit35yiz.png)

To access the `/users` on postman we need to register `UsersModule` in `AppModule` like so:

```js
...
import { UsersModule } from './users/users.module';

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

```
You might notice that nest already registered the module for you. Pretty neat!

Let's test our new endpoint by modifying `users.service`

```js
import { Injectable } from '@nestjs/common';

@Injectable()
export class UsersService {
  findAll(): string {
    return 'returns all users';
  }
}

```
Next, we update `users.controllers` by importing and adding `UsersService` to the list of constructors

```js
...
import { UsersService } from './users.service';

@Controller('users')
export class UsersController {
  constructor(private usersService: UsersService) {}

}

```
Finally, you can use the service like so

```js
import { Controller, Get } from '@nestjs/common';
import { UsersService } from './users.service';

@Controller('users')
export class UsersController {
  constructor(private usersService: UsersService) {}

  @Get()
  findAll(): string {
    return this.usersService.findAll();
  }
}

```

Let's test all we've done by hitting `http:localhost:3000/users` 


![nest-users](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/p6h8984uko8tw8zguki9.png)
Yay! It works!

In reality, we'd like to return actual data of the users. This is where `Prisma` comes in. 

## Setting up Prisma

Let's setup `Prisma` by running

```bash
yarn add prisma --dev

#init source provider
npx prisma init --datasource-provider sqlite
```
The datasource provider could be postgres, mysql or anything else but in our case we would be using sqlite.

The next bit is to set up our schema (this can also be seen as our database model). The last command we ran should have created a file in `prisma/schema.prisma`. Navigate to this file, you should see somethig like

```prisma
...

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

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


```
These are all fine and default code. It tells `Prisma` about what kind of DB we're using etc.

Now, we'll create our first model, the user model by adding the following block of code to our schema file

```prisma
...

model User {
  id    Int     @id @default(autoincrement())
  email String  @unique
  name  String?

  createdAt DateTime  @default(now())
  updatedAt DateTime  @updatedAt
  deletedAt DateTime?
}
```
A lot of things in `Prisma` are pretty intuitive and self-explanatory. The model name corresponds to the the table name in the db, the first column refers to the column names, the second column specifies the data type while the third column specifies additional information such as default values or relations.
`@id` means default id or primary key if you like, `@default` means a default value would  be added. `autoincrement` means that the primary key would automatically be incremented when there's a new entry into the database. `now` and `@updated` at are used to assign default time stamps to database entries.

Next we run `yarn prisma generate` to generate our models, we'd also run `yarn prisma db push` to keep our database in sync with our schema. You should see your db created if you're using sqlite like this

![yarn prisma db push](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tmsv35ri1frd75vk6g48.png)


 In order to manage changes to our database models, it makes sense to create migrations to keep track of incremental changes to our db. You can create a migration by running

```bash
yarn prisma migrate dev
```
enter `"y"`, then a name for your migration 
![prisma migration](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/s1eoe2m1anlllrcoor4d.png)

You should seen the migration file created. Let's take a look at the migration file to see and idea of what it looks like.

![sql of migration file](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xsfqfikqmqsrq0jzgp9z.png)

It's pretty much an sql file specifying the changes or in this case how to create the user table.

Woah! that's was a lot!

Now, before we can use our models in any of our services, we first need to create a `Prisma` service file, thus we'd be able to inject `Prisma` as a service to any module.

```bash
nest g service prisma
```

navigate to `prisma.service.ts` then replace the entire with the following block

```js

import { Injectable, OnModuleInit, INestApplication } from '@nestjs/common'
import { PrismaClient } from '@prisma/client'

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

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

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

```js
...
import { PrismaService } from './prisma/prisma.service';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);

  const prismaService = app.get(PrismaService);
  await prismaService.enableShutdownHooks(app);

  await app.listen(3000);
}
bootstrap();
```
Now, we have our `Prisma` service up and running. 

Next, we need to add `PrismaService` to the list of providers for the module then initialize `PrismaService` in the constructor of `UsersService` or any other service as needed like so

```js
// user.module

...
import { PrismaService } from 'src/prisma/prisma.service';

@Module({
  providers: [UsersService, PrismaService],
  controllers: [UsersController],
})
export class UsersModule {}


```

```js
...
import { PrismaService } from 'src/prisma/prisma.service';

@Injectable()
export class UsersService {
  constructor(private readonly prismaService: PrismaService) {}

  ...
}
```

now we can use `Prisma` like this:

```js
...
async findAll() {
    return await this.prismaService.user.findMany();
}
...
```
Voila! I'm sure you really enjoyed the type support you got.

In the end, the entire file should look something like this

```js
import { Injectable } from '@nestjs/common';
import { User } from '@prisma/client';
import { PrismaService } from 'src/prisma/prisma.service';

@Injectable()
export class UsersService {
  constructor(private readonly prismaService: PrismaService) {}

  async findAll(): Promise<User[]> {
    return await this.prismaService.user.findMany();
  }
}

```
Don't for get update the type in the controller file

```js
import { Controller, Get } from '@nestjs/common';
import { User } from '@prisma/client';
import { UsersService } from './users.service';

@Controller('users')
export class UsersController {
  constructor(private usersService: UsersService) {}

  @Get()
  async findAll(): Promise<User[]> {
    return await this.usersService.findAll();
  }
}

```

and there we have it

![successfully-getting-all-users](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3imc78l4kyymqr3yjkh3.png)

## Creating Data

Next, let's try to create new users. 

```js
// users.conntroller
@Controller('users')
export class UsersController {
  ...

  @Post()
  async create(@Body() createUserDto: CreateUserDto) {
    this.usersService.create(createUserDto);
  }
}

```
You'd notice something new `CreateUserDto`. `Dto`s are used to specify the structure of data to expect while `@body` implies that the data should be found in the body of the post request, they can also be used to along side [class-validator](https://www.npmjs.com/package/class-validator) library for validation.

First, we install some packages

```sh
yarn add class-validator class-transformer
```

Add validation support to our app like so 

```js
// main.ts
import { ValidationPipe } from '@nestjs/common';
...

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.useGlobalPipes(new ValidationPipe({ transform: true }));
  const prismaService = app.get(PrismaService);
  await prismaService.enableShutdownHooks(app);

  await app.listen(3000);
}
bootstrap();
```
Next, we create a new file called `create-user.dto.ts` in the `users` folder.


Now let's add the following block to the file 

```js
import { IsEmail, IsNotEmpty } from 'class-validator';

export class CreateUserDto {
  @IsNotEmpty()
  name: string;

  @IsEmail()
  email: string;
}

```
Notice that this matches our schema from `schema.prisma`?

Anyway, now we have `CreateUserDto`. The next bit is to update `user.service` to handle the creation.

```js
...
import { Prisma, User } from '@prisma/client';
...
import { CreateUserDto } from './create-user.dto';

@Injectable()
export class UsersService {
  constructor(private readonly prismaService: PrismaService) {}

  ...

  async create(createUserData: Prisma.UserCreateInput): Promise<User> {
    const { name, email } = createUserData;
    return await this.prismaService.user.create({
      data: {
        name,
        email,
      },
    });
  }
}

```
With this we should be able to create a new user with validation added!

![post-data-with-nest-js-with-validation](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lnwqypjl7hvrhq6xttnz.gif)



![class-validator-error-validation-with-nest-js](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/77bturh0l94r48ao0yw1.png)

## Getting single data from the db

Next, we're going to try to get a single user from the db by its `id`. Remember that every user in our db has a unique id which is also the primary key of the user table.

let's say we want the url `localhost:3000/users/3` to mean get the user with id=3, we'd do the following:

First, update `users.controller` with the following block

```js
import {
  ...
  Param,
  ParseIntPipe,
  Post,
} from '@nestjs/common';
...

@Controller('users')
export class UsersController {
  ...

  @Get(':id')
  async findOne(@Param('id', ParseIntPipe) id: number): Promise<User | null> {
    return await this.usersService.findUserById(id);
  }

  ...
}

```
Couple of interesting new things here, `id` specificies the param of interest, `ParseIntPipe` is a built-in pipe for validating numbers.

Next, we update `users.service` to include `findUserById`.

```js
import { Injectable, NotFoundException } from '@nestjs/common';
...

@Injectable()
export class UsersService {
  ...

  async findUserById(id: number): Promise<User | null> {
    const user = await this.prismaService.user.findUnique({
      where: {
        id,
      },
    });

    if (!user) {
      // optional, you can return null/undefined depending on your use case
      throw new NotFoundException();
    }

    return user;
  }

  ...
}

```
`NotFoundException` is one of the inbuilt exceptions in nestjs, it'll return this by default

![NotFoundException](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yhq2i5m457pimqz4bpyt.png)

Anyway, our code works!


![successfully getting single item nest prisma](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/s0ct52lvy2tvegc12s7q.png)

And the best part is that validation works out of the box!

## Relation in prisma
Let's create a new model item, users should be able to add and update items.

First, let's update `schema.prisma` with our new model

```schema
...

model User {
  id    Int     @id @default(autoincrement())
  email String  @unique
  name  String?

  items Item[] // one-to-many relation, i.e a user can have multiple items

  createdAt DateTime  @default(now())
  updatedAt DateTime  @updatedAt
  deletedAt DateTime?
}

model Item {
  id        Int      @id @default(autoincrement())
  name      String
  isForRent Boolean? @default(false)

  User   User? @relation(fields: [userId], references: [id])
  userId Int? // id for user who is associated with this item

  createdAt DateTime  @default(now())
  updatedAt DateTime  @updatedAt
  deletedAt DateTime?
}

```

Don't forget to create the migration with `yarn prisma migrate dev`.

Now let's create the controller, module and service files for `item`. Remembers this boring old command from before `nest g service users && nest g controller users && nest g module users`? Well, there's a better way to do it. 

```sh
nest g resource
```
This would help you generate all the files you need including `Dto`s and test files. 

![nest g resource](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4fvzatekkchngc2d9nhf.png)
We selected rest api for our case here but it can be any of the options

Now, let's create our items.

The last command we ran should have created a file called `create-item.dto.ts`. If it did not, create one. Now, we update this file to look like this 

```js
import { IsBoolean, IsInt, IsNotEmpty, IsOptional } from 'class-validator';

export class CreateItemDto {
  @IsNotEmpty()
  name: string;

  @IsOptional()
  @IsBoolean()
  isForRent: boolean;


  // In this example we want the user id to be part of the payload. This isn't always the case
  @IsInt()
  userId: number;
}

```

Notice how self-explanatory class-validator is?

To use `Prisma` in `items.service` we have to add `PrismaService` to the list of providers in `ItemsModule`. Update `items.module` to look like this

```js
// items.module.ts

import { Module } from '@nestjs/common';
import { ItemsService } from './items.service';
import { ItemsController } from './items.controller';
import { PrismaService } from 'src/prisma/prisma.service';

@Module({
  controllers: [ItemsController],
  providers: [ItemsService, PrismaService],
})
export class ItemsModule {}

```

`items.controller.ts` should already have the block below, if not add it

```js
import {
  Controller,
  Get,
  Post,
  Body,
  Patch,
  Param,
  Delete,
} from '@nestjs/common';
import { ItemsService } from './items.service';
import { CreateItemDto } from './dto/create-item.dto';
import { UpdateItemDto } from './dto/update-item.dto';

@Controller('items')
export class ItemsController {
  constructor(private readonly itemsService: ItemsService) {}

  @Post()
  async create(@Body() createItemDto: CreateItemDto) {
    return this.itemsService.create(createItemDto);
  }
  
 ...
}


```

Now the most important part, the actual creation logic in the service file. Let's do that by adding `PrismaService` to the constructor of `ItemsService`

```js
import { Injectable } from '@nestjs/common';
import { Item } from '@prisma/client';
import { PrismaService } from 'src/prisma/prisma.service';
import { CreateItemDto } from './dto/create-item.dto';
import { UpdateItemDto } from './dto/update-item.dto';

@Injectable()
export class ItemsService {
  constructor(private readonly prismaService: PrismaService) {}

  async create(createItemData: CreateItemDto): Promise<Item> {
  ...
}
  ...
}

```
There are three ways to add relation data and the all work to the best of knowledge.

```js
// 1
async create(createItemData: CreateItemDto): Promise<Item> {
    const { name, isForRent, userId } = createItemData;
    const user = await this.prismaService.user.update({
      where: {
        id: userId,
      },
      data: {
        items: {
          create: {
            name,
            isForRent,
          },
        },
      },
      include: {
        items: {
          take: -1,
        },
      },
    });

    return user.items[0];
  }
```

```js
// 2

async create(createItemData: CreateItemDto): Promise<Item> {
    const { name, isForRent, userId } = createItemData;
    const item = await this.prismaService.item.create({
      data: {
        name,
        isForRent,
        User: {
          connect: {
            id: userId,
          },
        },
      },
    });

    return item;
  }
```

```js
// 3

async create(createItemData: CreateItemDto): Promise<Item> {
    const { name, isForRent, userId } = createItemData;
    const item = await this.prismaService.item.create({
      data: {
        name,
        isForRent,
        userId,
      },
    });

    return item;
  }

```

I personally prefer the first method but it's up to you to choose what you prefer.


![successfully entering related data with prisma on nest js](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1pd67rdq5owlq2zg7j1o.png)

## Getting related data

Let's say we want to get all the items connected to a certain `userId`. and also let's say we want the url to be something like this: `http://localhost:3000/items?userId=1`

First we'd update the controller to include the following block

```js
// items.controller.ts

import {
  ...
  ParseIntPipe,
} from '@nestjs/common';
...

@Controller('items')
export class ItemsController {

  ...

  @Get()
  async findAllByUserId(@Query('userId', ParseIntPipe) userId: number) {
    return this.itemsService.findAllByUserId(userId);
  }

  ...
}

```
Next, let's add our query to `ItemsService`.

```js
...
import { Item, Prisma } from '@prisma/client';
...

@Injectable()
export class ItemsService {
  ...

  async findAllByUserId(userId: number): Promise<Item[]> {
    return await this.prismaService.item.findMany({
      where: {
        userId,
      },
    });
  }

 ...
}

```

And it works!


![nest-prisma-get-items-by-id](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/i8y2p53r54peh1r195ox.png)

## Updating things

Finally, we're going to update an item. Add the following block to `items.controller`

```js
import {
  ...
  ParseIntPipe,
  Put,
} from '@nestjs/common';
import { UpdateItemDto } from './dto/update-item.dto';
...

@Controller('items')
export class ItemsController {
  ...

  @Put(':id')
  async update(
    @Param('id', ParseIntPipe) id: number,
    @Body() updateItemDto: UpdateItemDto,
  ) {
    return this.itemsService.update(id, updateItemDto);
  }

  ...
}

```
`UpdateItemDto` should also have been generated by `nest g resource` and it should be something like this

```js
// update-item.dto.ts
import { PartialType } from '@nestjs/mapped-types';
import { CreateItemDto } from './create-item.dto';

export class UpdateItemDto extends PartialType(CreateItemDto) {}

```
In the service file, we add the following

```js
// items.service.ts
...
import { Item } from '@prisma/client';
..

@Injectable()
export class ItemsService {
  ...

  async update(id: number, updateItemDto: UpdateItemDto): Promise<Item> {
    const { name, isForRent } = updateItemDto;
    return await this.prismaService.item.update({
      where: {
        id,
      },
      data: {
        name,
        isForRent,
      },
    });
  }

  ...
}

```

And we're done, notice how we're building endpoints and services faster now? That's just the power of NestJs and Prisma together.

Anyway, we're done!

![nest-create-item](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/h72sra0kzuxt6xrl8nsu.png)

![nest-updating-item-with-prisma](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2kilp4hy1o2lp5v0dhdw.png)


Feel free to play around with the apis until you get really comfortable with it. Do let me know how much you've enjoyed using these tools in the comment.


## Useful links

1. [Github repo](https://github.com/majiyd/nest-prisma-basic-tutorial)
2. [https://nestjs.com/](https://nestjs.com/)
3. [https://www.prisma.io/](https://www.prisma.io/)
4. [https://www.fullstackpython.com/object-relational-mappers-orms.html](https://www.fullstackpython.com/object-relational-mappers-orms.html)