avatarKagklis Vasileios

Summary

The article provides a guide on enhancing a NestJS backend application by adding field validations, hashing passwords before storing them in the database, and creating scripts for automating repetitive actions such as PostgreSQL restart and Prisma migrations.

Abstract

In the continuation of a previous article, the author delves into the process of fortifying a NestJS backend application. The article begins by recapping the creation of a NestJS application with Prisma as the ORM and a PostgreSQL database managed through Docker. It then proceeds to demonstrate how to implement field validations using NestJS's built-in ValidationPipe in conjunction with class-validator and class-transformer packages. The author emphasizes the importance of hashing passwords to ensure security, recommending the use of argon2 for this purpose. The article also covers the creation of NodeJS scripts to streamline the development process by automating database-related tasks, such as restarting PostgreSQL and applying Prisma migrations. These enhancements contribute to a more robust, secure, and developer-friendly backend application.

Opinions

  • The author suggests that manually validating each property in a DTO becomes tedious and verbose, advocating for the use of NestJS's built-in ValidationPipe to automate this process.
  • Storing plain text passwords in a database is highlighted as a critical security flaw, with the author urging the adoption of password hashing as a best practice.
  • The use of argon2 for password hashing is recommended over other solutions like bcrypt, though the reasons for this preference are not explicitly stated.
  • The author expresses that automating repetitive actions such as database restarts and migrations can save developers significant time and effort during the development cycle.
  • The inclusion of a sleep command in the automation scripts is acknowledged as a workaround for concurrency issues, with a disclaimer for Windows users to use an alternative approach if necessary.
  • The author encourages readers to subscribe to their newsletter for ongoing content, indicating a commitment to providing further educational resources.

How to Build a Backend with NestJS and Prisma — Part 2

Learn how to add field validations to a NestJS backend application, hash your passwords before storing them in the database, and automate repetitive actions with NodeJS scripts.

Photo by Mikhail Vasilyev on Unsplash

In our previous article, we created a NestJS backend application. We configured Prisma as the ORM and set up a PostgreSQL database in a Docker container.

In this article, we will enhance our NestJS backend by:

  • adding field validations
  • hashing the password before storing it in the database
  • creating scripts to automate repetitive actions, such as PostgreSQL restart and Prisma migrations

Let’s get started!

Adding Field Validations to NestJS Backend

Recall that our AuthController looks like this:

@Controller('auth')
export class AuthController {
  constructor(private authService: AuthService) {}

  @HttpCode(HttpStatus.CREATED)
  @Post('register')
  register(@Body() dto: RegisterDTO) {
    return this.authService.register(dto);
  }
}

We could write if blocks inside the register method to validate each property in the DTO, but that becomes tedious and verbose quickly.

The official documentation states:

To automatically validate incoming requests, Nest provides several pipes available right out-of-the-box […].

Like in Angular, pipes in NestJS are functions that transform your data.

To use the built-in ValidationPipe we need the class-transformer and class-validator packages.

To install them, run:

npm install class-validator class-transformer

Also, if you defined your DTO as an interface, you will have to convert it into a class. Simply replace the keyword interface with class.

Next, we need to decorate the properties of the DTO accordingly:

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

export class RegisterDTO {
  @IsEmail()
  @IsNotEmpty()
  email: string;

  @IsString()
  @IsNotEmpty()
  password: string;

  @IsString()
  @IsNotEmpty()
  firstName: string;

  @IsString()
  @IsNotEmpty()
  lastName: string;
}

The decorators we used are pretty self-explanatory.

But class-validator provides a lot of such decorators. Here is the full list of decorators and what each one does.

As a last step, we need to tell Nest to use the ValidationPipe globally. To do so, we need to call the useGlobalPipes and pass an instance in main.ts:

import { ValidationPipe } from '@nestjs/common';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  // ...
  app.useGlobalPipes(
    new ValidationPipe({
      whitelist: true,
    }),
  );
  await app.listen(3000);
}

Note that you need to call this method before the app.listen(3000), otherwise it won’t work as expected.

The whitelist: true option tells the validator to strip any properties without a decorator. This is to remove any extra properties passed in the request’s payload.

Try it out with an invalid email address:

We get an error Bad Request with an informative message.

Hashing the Password Before Storing It

So far, our NestJS backend application stores the password as plain text in the database.

That is a terrible idea that’s asking for trouble.

Let’s fix this!

There are plenty of hashing solutions out there like bcrypt and argon2. We will use the latter.

To install it, run:

npm install argon2

In the AuthService, instead of passing the password straight from the DTO, we first hash it:

@Injectable()
export class AuthService {
  constructor(private prisma: PrismaService) {}

  async register(dto: RegisterDTO) {
    try {
      const hash = await argon.hash(dto.password);
      const user = await this.prisma.user.create({
        data: {
          email: dto.email,
          hash,
          firstName: dto.firstName,
          lastName: dto.lastName,
        },
        select: {
          id: true,
          email: true,
          createdAt: true,
        }
      });
      return user;
    } catch (error) {
      // ...
    }
  }

}

We use the select block to return only specific properties of the user. So it’s safe to return the user. Let’s do that instead of printing the information to the console.

Hit the /register endpoint and inspect the row in the database using Prisma Studio:

Now the password is hashed!

Automating Postgres Restart & Prisma Migrations

During development, you may have to reset or restart the database. And then you will also need to run the migrations.

It’s much easier and handy if we have scripts that automate all those actions. These scripts will save you a lot of time.

Firstly, we want to remove the container:

docker compose rm dev-db -s -f -v

Option -f is for forced, -s is for stopping the container before removing, and -v is to also remove anonymous volumes attached to the container.

Then, we need to recreate the container:

docker compose up dev-db -d

Option -d means run in the background.

Finally, we want to run the migrations:

prisma migrate deploy

Let’s put these commands in the package.json file, in the scripts block:

scripts": {
  "prisma:deploy": "prisma migrate deploy",
  "dev-db:rm": "docker compose rm dev-db -s -f -v",
  "dev-db:up": "docker compose up dev-db -d",
  "dev-db:restart": "npm run dev-db:rm && npm run dev-db:up && sleep 1 && npm run prisma:deploy",
  // ... Other scripts ...
}

Notice that we have an extra line (dev-db:restart) that combines all previous commands.

Disclaimer: The added sleep 1 is there for concurrency reasons. However, if you are using Windows, sleep won’t be recognized in the PowerShell. Either run this from a Git Bash or remove the sleep and try to run it.

Next time you wish to rebuild your database from scratch, run:

npm run dev-db:restart

You can find the source code of the demo in this GitHub repository.

Conclusion

In this article, we enhanced our NestJS backend application.

We added field validations by using the built-in ValidationPipe. We hashed the password with argon2 before storing it in the database.

Lastly, we automated repetitive actions by creating script commands.

That’s all for now.

Subscribe to my newsletter and stay tuned for more content like this!

Thank you for reading!

Nestjs
Nodejs
JavaScript
Web Development
Programming
Recommended from ReadMedium