Building A Robust Backend Server With NestJS 🚧
Tags: nestjs, typescript, prisma, postgres
Published: 2021-10-25 06:00:30

> _It's time to talk about it... Not ~~Next~~, nor ~~Nuxt~~, but **Nest**!_

### P r o l o g u e

Some time ago, I challenged myself to build a robust backend solution in JavaScript. Many developers believe that JavaScript isn't secure enough, efficient or simply organized to manage data through APIs. I'll convince you otherwise....

For this new project, I chose technologies that are on the rise, in particular [PostgreSQL](https://www.postgresql.org) but especially [NestJS](https://nestjs.com). So, I gave up my good old database that is MongoDB, in favor of a more traditional database.

> _**NB**: I was a little bit rusty at first, but going back to relational database was a real good idea; if only to retrieve data from several tables in one call, unlike NoSQL which requires as many calls as there are documents to query... Finally, SQL is like riding a bike, it's unforgettable_ 😉

NestJS is a framework for creating high-scalability server applications (powered by NodeJS). This one is based on Typescript and combines three development paradigms:
- The **O**bject **O**riented **P**rogramming #**OOP**
- The **F**unctional **P**rogramming #**FP**
- The **F**unctional **R**eactive **P**rogramming #**FRP**

NestJS starts from the observation that Web frameworks (especially those component-oriented), aren't structured enough... Except one: Angular! So, it's inspired by this last one to provide a complete library for backend developments.

> _**NB**: Indeed, Angular has a similar organization, but unlike NestJS, the structure of this one is (in my opinion) more restrictive than anything else... I prefer its counterparts like React and Vue (and Svelte) which offer more flexibility in frontend application design. However, I'm convinced that this "heavy" architecture can be a substantial help to those who are new to frontend development._

Finally, NestJS embeds the [ExpressJS](https://expressjs.com) engine or that of [Fastify](https://www.fastify.io) (at choice), to deliver a robust and powerful HTTP server. So, you can talk about a framework of frameworks (meta-framework!? 🤔) For this project, I chose to use Fastify which seems more than promising...

## I n i t i a l i z a t i o n

It's time to code! 🧑‍💻 Let's start by installing the main NestJS dependency, then initialize a new project: `hello-community`

```
npm i -g @nestjs/cli
nest new hello-community
```

So, do you see the similarities with Angular? 🙃 Using the NestJS command line interface, you get an "**modules** - **controllers** - **services**" architecture:
- **Controllers** are responsible for exposing application routes
- **Services** handle communication between data sources
- **Modules** link services with controllers

> _**NB**: In addition to initializing the project, the NestJS CLI will allow you to individually scaffold these same files (`nest generate <module|controller|service>`). So useful!_

Now, let's take a closer look at the `main.ts` file, entry point of the application. By default, NestJS will mount an HTTP server based on ExpressJS. But, in my case, I'm gonna need Fastify, which (let's remember) is supposed to be faster and lighter than its counterpart.

To do this, it's necessary to add the `@nestjs/platform-fastify` dependency to the project, then adapt the `main.ts` file code.

```ts
import { NestFactory } from '@nestjs/core';
import { FastifyAdapter, NestFastifyApplication } from '@nestjs/platform-fastify';
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.create<NestFastifyApplication>(
    AppModule,
    new FastifyAdapter()
  );
  await app.listen(3000);
}

bootstrap();
```

<figcaption>main.ts</figcaption>

Et voilà ! Just go to `http://localhost:3000`, to see your first `Hello World!` (or `Hello Community!` if you've been curious) 👏

> _**NB**: By specifying the `0.0.0.0` string as a second parameter of the `listen()` function, you'll be able to access your endpoints within the same local network._

It's hard to see a real difference between ExpressJS and Fastify on a simple call. Although... I noticed a small difference when loading. According to Google Chrome, this first `Hello World!` loads 239 bytes with **ExpressJS**, versus 176 bytes via **Fastify** (confirmed by Mozilla Firefox). It's clear that this framework is (at least) lighter than its predecessor.

## C R U D

Let's make things more interesting / more concrete by implementing your first REST(ful) API. For that, you can use the CLI tool to create files one by one (module / controller / service), or generate the whole set directly (i.e. a new CRUD resource).

The `nest g resource` command does this for you. By typing the resource name (`users`) and specifying that it's a REST API, you quickly get a ready-to-use controller and service.

One of benefits of this mode of operation is that NestJS also generates some development patterns, including the well-known **DTO** (**D**ata **T**ransfer **O**bject) of developers. #**OOP**

```ts
import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
import { CreateUserDto } from './dto/create-user.dto';
import { UpdateUserDto } from './dto/update-user.dto';
import { User } from './interfaces/user.interface';
import { generateUuid } from '../utils';

@Injectable()
export class UsersService {
  users: User[] = [];

  findAll(): User[] {
    return this.users;
  }

  findOne(id: string): User {
    const user = this.users.find(user => user.id === id);
    if (user) return user;
    throw new HttpException('No User Found', HttpStatus.NOT_FOUND);
  }

  findOneByEmail(email: string): User {
    const user = this.users.find(user => user.email === email);
    if (user) return user;
    throw new HttpException('No User Found', HttpStatus.NOT_FOUND);
  }

  create(createUserDto: CreateUserDto): { createdId: string } {
    const uuid = generateUuid();
    this.users = [
      ...this.users,
      {
        id: uuid,
        ...createUserDto
      }
    ];
    return { createdId: uuid };
  }

  update(id: string, updateUserDto: UpdateUserDto): { updatedId: string } {
    let founded = false;
    this.users = this.users.map(user => {
      if (user.id === id) {
        founded = true;
        return { ...user, ...updateUserDto };
      }
      return user;
    });
    if (founded) return { updatedId: id };
    throw new HttpException('No User Found', HttpStatus.NOT_FOUND);
  }

  remove(id: string): { removedId: string } {
    const length = this.users.length;
    this.users = this.users.filter(user => user.id !== id);
    if (length !== this.users.length) return { removedId: id };
    throw new HttpException('No User Found', HttpStatus.NOT_FOUND);
  }
}
```

<figcaption>users.controller.ts</figcaption>

For this first resource, I suggest you to keep the controller as it is (`users.controller.ts`), but adapt the service code (`users.service.ts`), in order to simulate a data source.

> _**NB**: For practical reasons (prevent the size of this post from growing fast), I chose to show the type of my data. In a real case, I advise you to put it in the right places, namely in `interfaces` and `enums` (or simply `models`) folders._

Make sure you have registered your `users.module.ts` module at the main module level (`app.module.ts`), then launch the application (`npm run start`). You should be able to start playing with your API via the REST protocol (and [Postman](https://www.postman.com) of course 😎).

## S e c u r i t y

Serious things start here... If you go back quickly, you notice that your endpoints are all reachable with the same level of security (that is to say none). Furthermore, the user's password is directly saved in the database... So, it's necessary to secure all of this!

First step: the password obfuscation. NestJS supports data encryption (with the NodeJS **crypto** module), but also data hashing. Here, I chose to use the **bcrypt** library to hash my strings, to guarantee unidirectional (and optimal) security! 👌

Let's install dependencies (`npm i --save bcrypt && npm i --save-dev @types/bcrypt`) and modify the user creation service to secure its password.

```ts
import * as bcrypt from 'bcrypt';

@Injectable()
export class UsersService {
  // ...

  async create(createUserDto: CreateUserDto): Promise<{ createdId: string }> {
    const uuid = generateUuid();

    const salt = await bcrypt.genSalt();
    const hash = await bcrypt.hash(password, salt);

    this.users = [
      ...this.users,
      {
        id: uuid,
        password: hash,
        ...createUserDto
      }
    ];
    return { createdId: uuid };
  }

  // ...
}
```

<figcaption>users.service.ts</figcaption>

Second step: securing calls using an authentication protocol. This second task is more complex since it requires the implementation of one or more authentication strategies. Fortunately, NestJS already has a "_guard_" concept allowing to "**validate** / **invalidate**" routes of the application. Let's go!

```
npm install --save @nestjs/passport passport passport-local passport-jwt
npm install --save-dev @types/passport-local @types/passport-jwt
```

The best way to establish this kind of operation (with NodeJS) is to use [Passport](http://www.passportjs.org). On the design side, I'll have to create a new authentication route that will provide me a token to validate `/users/:id` endpoints (GET, PATCH and DELETE). That's why, I recover (above) two strategies:
- The **LOCAL** strategy, allows simple authentication (based on the `username` / `password` pair)
- The **JWT** strategy, is based on the validity of a token over time

Again, you'll have to use the command line interface to instantiate a module, a controller and a service (`nest g <module|controller|service> auth`). Furthermore, you will have to create two new files corresponding to the Passport strategies respectively.

```ts
import { Injectable, UnauthorizedException } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
import { Strategy } from 'passport-local';
import { User } from '../users/interfaces/user.interface';
import { AuthService } from './auth.service';

@Injectable()
export class LocalStrategy extends PassportStrategy(Strategy) {
  constructor(private authService: AuthService) {
    super({
      usernameField: 'email'
    });
  }

  async validate(email: string, password: string): Promise<User> {
    const user = await this.authService.validateUser(email, password);
    if (user) return user;
    throw new UnauthorizedException();
  }
}
```

<figcaption>local.strategy.ts</figcaption>

> _**NB**: Here, you'll notice that I replaced the default behavior of the **LOCAL** strategy, to use the `email` / `password` pair._

```ts
import { Injectable } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
import { ExtractJwt, Strategy } from 'passport-jwt';

interface Payload {
  id: string;
  email: string;
  iat: string;
  exp: string;
}

@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
  constructor() {
    super({
      jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
      ignoreExpiration: false,
      secretOrKey: process.env.JWT_SECRET
    });
  }

  async validate(payload: Payload) {
    return { userId: payload.id, userEmail: payload.email };
  }
}
```

<figcaption>jwt.strategy.ts</figcaption>

> _**NB**: In a real case, I'd advise against exposing the JWT secret! It's better to fetch it in another way, through the file `.env` (the `@nestjs/config` module can then be useful) or another process..._

```ts
import { Injectable } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import * as bcrypt from 'bcrypt';
import { User } from '../users/interfaces/user.interface';
import { UsersService } from '../users/users.service';

type UserOrNull = User | null;

@Injectable()
export class AuthService {
  constructor(private usersService: UsersService, private jwtService: JwtService) {}

  async validateUser(email: string, password: string): Promise<UserOrNull> {
    const user = await this.usersService.findOneByEmail(email);
    const isMatch = await bcrypt.compare(password, user.password);

    if (user && isMatch) {
      return user;
    }
    return null;
  }

  async login(user: { id: string; email: string; }) {
    const payload = { userId: user.id, userEmail: user.email };

    return {
      access_token: this.jwtService.sign(payload)
    };
  }
}
```

<figcaption>auth.service.ts</figcaption>

```ts
import { Controller, Post, Request, UseGuards } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
import { AuthService } from './auth.service';

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

  @UseGuards(AuthGuard('local'))
  @Post('login')
  async login(@Request() req) {
    return this.authService.login(req.user);
  }
}
```

<figcaption>auth.controller.ts</figcaption>

```ts
import { Module } from '@nestjs/common';
import { JwtModule } from '@nestjs/jwt';
import { PassportModule } from '@nestjs/passport';
import { UsersModule } from '../users/users.module';
import { AuthController } from './auth.controller';
import { AuthService } from './auth.service';
import { JwtStrategy } from './jwt.strategy';
import { LocalStrategy } from './local.strategy';

@Module({
  imports: [
    PassportModule,
    JwtModule.register({
      secret: 'Hello_Community',
      signOptions: { expiresIn: '300s' }
    }),
    UsersModule
  ],
  controllers: [AuthController],
  providers: [AuthService, LocalStrategy, JwtStrategy],
  exports: [AuthService]
})
export class AuthModule {}
```

<figcaption>auth.module.ts</figcaption>

After all this, some explanations are mandatory. Let’s start with the controller...

When calling the `/login` endpoint, the user will "_POST_" his credentials in `email` / `password` format.

Then, NestJS will protect the route by the **LOCAL** "_guard_" which will take care of checking the user presence into the database. If the user with the unique email address is found, the password will then be checked by **bcrypt** (`auth.service.ts`).

If things go well, the `login()` function will be played with the information relating to the user (especially the unique identifier and the email) which will be necessary to generate a token with 5 minutes (`300s`) validity.

Finally, at the endpoint return, you should retrieve a JWT token (`access_token`) which will authenticate the other calls of your REST API. To do this, two final things:
- Add the **JWT** "_guard_" on the `/users/:id` endpoints (`users.controller.ts`)
- Add the `Bearer <token>` in the header of the call(s)

## P R I S M A

Now let's talk about the data part. From now on, you will have to connect to your database (previously installed on your machine: `sudo apt install postgresql`), then query it in a REST(ful) way, namely with GET, POST, PATCH, DELETE, etc...

One last time, a difficult choice is required, between:
- The database driver (low level query)
- The "_Query Builder_" (such as [KnexJS](https://knexjs.org))
- The **ORM** - **O**bject **R**elational **M**apping (high level query)

> _**NB**: As an example, with MongoDB, I used to develop with Mongoose (which is an **ODM** - **O**bject **D**ocument **M**apper) to query my NoSQL data source. Having made the choice of SQL with Postgres, it will be essential to use a new library..._

Don't panic! NestJS is here 🎉 And it already has "_connectors_" for some libraries, including: Sequelize, TypeORM or even Prisma. I preferred to exclude Sequelize from the challengers list (despite its popularity), since I want to be able to re-use the "_chosen one_" for future development with NoSQL (I can't live without Mongo 😅). In fact, the only possible choice is none other than [Prisma](https://www.prisma.io). By the way, I was pleasantly surprised by its concept and its intuitive API! It's time to install this new dependency, and then initialize the database schema.

```
npm install prisma --save-dev
npx prisma init
```

By executing Prisma's `init` command, you should see a new folder appear at the root of your project, with a first draft of the schema. I invite you to complete it as follows:

```
datasource db {
  provider = "postgresql"
  url      = "postgresql://<username>:<password>@<host>:<post>/hello-community?schema=public"
}

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

enum Gender {
  X
  Y
}

model User {
  id        String  @id @default(uuid())
  email     String  @unique
  password  String
  firstName String?
  lastName  String?
  gender    Gender
}
```

<figcaption>schema.prisma</figcaption>

As explicit as it is, the database schema will allow you to initialize your PostgreSQL database. Clearly, by executing this last one (with the `migrate` command), Prisma will transcribe the information above in SQL script, to create your tables (with the right fields and the right types) and add your relations (primary and foreign keys). It will eventually run the query directly into the database.

```
npx prisma migrate dev --name init
```

> _**NB**: From there, your base is created. You can connect to it and see the result; or take a look at the SQL query present in the `migration.sql` file (in the `prisma` folder)._

```
npm i @prisma/client
nest g module prisma
nest g service prisma
```

The database is properly created, but you still have to make the link between PostgreSQL (via Prisma) and NestJS. Using the NestJS CLI tool (above), I generated two new files:
- `prisma.module.ts`
- `prisma.service.ts`

```ts
import { Module } from '@nestjs/common';
import { PrismaService } from './prisma.service';

@Module({
  providers: [PrismaService],
  exports: [PrismaService]
})
export class PrismaModule {}
```

<figcaption>prisma.module.ts</figcaption>

```ts
import { Injectable, OnModuleDestroy, OnModuleInit } from '@nestjs/common';
import { PrismaClient } from '@prisma/client';

@Injectable()
export class PrismaService extends PrismaClient implements OnModuleInit, OnModuleDestroy {
  async onModuleInit() {
    await this.$connect();
  }

  async onModuleDestroy() {
    await this.$disconnect();
  }
}
```

<figcaption>prisma.service.ts</figcaption>

One last step: usage! Do you remember your CRUD above? Let's make it evolved! Now, there is no longer any question of simulating your database, let's request it directly. For that, the Prisma API is simply magical! It generates (thanks to the database schema) the types corresponding to `models`, and provides you with intuitive functions to query your SQL database. See for yourself the result with functions for creating and retrieving a user.

> _**NB**: Don't forget to register your new module (`prisma.module.ts`) in the `users` module imports, otherwise it's impossible to request your data source..._

```ts
import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
import { User, Prisma } from '@prisma/client';
import * as bcrypt from 'bcrypt';
import { PrismaService } from '../prisma/prisma.service';

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

  // ...

  async findOne(id: string): Promise<User> {
    const user = await this.prisma.user.findUnique({ where: { id } });
    if (user) return user;
    throw new HttpException('No User Found', HttpStatus.NOT_FOUND);
  }

  // ...

  async create({ password, ...userCreateInput }: Prisma.UserCreateInput): Promise<{ createdId: string }> {
    const salt = await bcrypt.genSalt();
    const hash = await bcrypt.hash(password, salt);

    const user = await this.prisma.user.create({
      data: {
        password: hash,
        ...userCreateInput
      }
    });

    return { createdId: user.id };
  }

  // ...
}
```

<figcaption>users.service.ts</figcaption>

> _**NB**: For practical reasons (prevent the size of this post from growing fast), I present here only two functions present in the service, but you can consult the rest of the code on [GitHub](https://github.com/dmnchzl/hellocommunity/blob/main/src/users/users.service.ts)._

### E p i l o g u e

NestJS is a little nugget of Web development! I had a lot of fun developing this new backend solution in TypeScript. Everything is very well structured. By the way, the CLI is a great help when it comes to scaffold all or part of a REST resource.

Overall, I was pleasantly surprised by its organization, which makes me think of the frameworks such as SpringBoot (for Java) or Django (for Python). The files are stored in their place and you find yourself there. It just goes to show that the MVC pattern is always a safe bet...

> _**NB**: In a more objective way, I asked the opinion of another developer specialized in Java, and I assure you, he had no problem to immerse himself in the code (thanks to TypeScript)._

Speaking of security, NestJS respects its part of the contract without reinventing things, since it relies mostly on the Passport dependency to consolidate its endpoints. It can also define the HTTP headers security by simply integrating middlewares provided by the Helmet library. The same for encryption and data hashing, these concepts aren't new and exist in other backend frameworks as well.

I didn't talk about unit tests previously, but they are present in this project. NestJS also makes a good choice by "_dropping_" the Karma / Jasmine pair in favor of Jest. It's very easy to mock Prisma data sources, to focus on controller calls, or on the function of a service.

By having powerful concepts and well-done documentation NestJS stands out as a reference in backend JavaScript development. It's a customizable framework (by embracing the **ExpressJS** engine, or switching to that of **Fastify**), and yet very complete with its lots of plugins, such as the possibility to add OpenAPI documentation via Swagger, or add GraphQL abstraction to your REST(ful) calls, etc...

In the future, I'll not longer hesitate in my choice of technical base for NodeJS, I'll simply opt for NestJS 👍

### S o u r c e C o d e

- [https://github.com/dmnchzl/hellocommunity](https://github.com/dmnchzl/hellocommunity)