• Sin categorizar

Getting Started with NestJS & SequilizeJS

Preface

Coming from Laravel, which has a myriad of built-in features I started to resent shifting to NodeJS. Setting up error handling, logging, dependency injection, etc. was not what I had in mind. Thankfully I found NestJS, with its out-of-the-box architecture and great documentation I started feeling at home again.

SequelizeJS is an ORM that offers connectivity to relational databases like MySQL, PostgreSQL, and MSSQL. For this article, I’m going to use MySQL hosted on RemoteMySQL, but you can use any relational database you like.

Getting Started

Assuming you have a nest project ready to go. We’ll start by installing the following dependencies.

npm install --save sequelize sequelize-typescript mysql2
npm install --save-dev @types/sequelize

First, we’ll pass the connection details to SequelizeJS. We can do this by creating a database module and provider.

nest generate module database
nest generate provider database/database.providers

This is where we will add our entity models to SequelizeJS. I’m adding models right now (even though they are created yet), but you can do this later.

import { Module } from "@nestjs/common"
import { databaseProviders } from "./database.providers"
@Module({
  providers: [...databaseProviders],
  exports: [...databaseProviders],
})
export class DatabaseModule {}

I have imported and added the user model to the addModels function. Now export your database provider so it can be consumed with any module that needs to access the database through SequelizeJS.

import { User } from "./user.entity"
import { USER_REPOSITORY } from "../utils/constants"
export const userProviders = [
  {
    provide: USER_REPOSITORY,
    useValue: User,
  },
]

User Entity Model

import { Injectable, Inject } from "@nestjs/common"
import { USER_REPOSITORY } from "../utils/constants"
import { User } from "./user.entity"
import { CreateUserDto } from "./dto/index"
@Injectable()
export class UserService {
  constructor(
    @Inject(USER_REPOSITORY) private readonly userRepository: typeof User
  ) {}
  async getAllUsers(): Promise<User[]> {
    return await this.userRepository.findAll<User>()
  }
  async createUser(createUser: CreateUserDto): Promise<User> {
    return await this.userRepository.create<User>(createUser)
  }
}

I’m not going to explain how the code above populates the database table and its attributes. If you’re interested in learning more about SequelizeJS, you can look here.

Next, we’ll create user.provider.ts which will be used to export the User model so it can be used in different services.

import { User } from "./user.entity"
import { USER_REPOSITORY } from "../utils/constants"
export const userProviders = [
  {
    provide: USER_REPOSITORY,
    useValue: User,
  },
]

The USER_REPOSITORY is stored in a const variable, in a separate file, so it can be used anywhere without being subject to human error.

At this point, we’re done with our database and SequelizeJS configuration. From now on it’s just a matter of importing database and its models and using them 😀.

Onwards with the Code

Let’s move on and create our user module, controller and service with the following command.

nest generate module user
nest generate controller user
nest generate service user

These are the files responsible for entertaining recurring database requests. But first we’ll create a Data Transfer Object (DTO), this is especially useful for validating body of the incoming HTTP request or building API documentation with swagger, etc.

export class CreateUserDto {
  readonly name: string
  readonly age: number
  readonly email: string
}

User Module

import { Module } from "@nestjs/common"
import { UserService } from "./user.service"
import { UserController } from "./user.controller"
import { DatabaseModule } from "../database/database.module"
import { userProviders } from "./user.providers"
@Module({
  imports: [DatabaseModule],
  controllers: [UserController],
  providers: [UserService, ...userProviders],
})
export class UserModule {}

The above code is consolidating all the User code (controller, service, model) into one place, the user module so it can be exported to the app module.

Note that the user controller and service have been generated but are empty right at this step. You can opt to populate this file later on.

User Service

import { Injectable, Inject } from "@nestjs/common"
import { USER_REPOSITORY } from "../utils/constants"
import { User } from "./user.entity"
import { CreateUserDto } from "./dto/index"
@Injectable()
export class UserService {
  constructor(
    @Inject(USER_REPOSITORY) private readonly userRepository: typeof User
  ) {}
  async getAllUsers(): Promise<User[]> {
    return await this.userRepository.findAll<User>()
  }
  async createUser(createUser: CreateUserDto): Promise<User> {
    return await this.userRepository.create<User>(createUser)
  }
}

Unlike user service which uses the “Injectable” decorator, the user provider we created to use the User Model is not a part of NestJS, therefore has to be injected manually.

We do this inside the service’s constructor method using the “Inject” decorator.

User Controller

import { Controller, Get, Post, Body } from "@nestjs/common"
import { UserService } from "./user.service"
import { User } from "./user.entity"
@Controller("user")
export class UserController {
  constructor(private readonly userService: UserService) {}
  @Get()
  public async getUsers(): Promise<User[]> {
    return this.userService.getAllUsers()
  }
  @Post()
  public async createUser(@Body() body): Promise<User> {
    return this.userService.createUser(body)
  }
}

The last step is to inject the user service in our user controller. The controller exposes our code base to externally accessible API endpoints.

If you’re curious, this is how my folder structure looks like.

The database connection details are in the database folder, easy to maintain and reuse anywhere in the app.

The bulk of the files are in the user folder. You can ignore the .spec files as they are used to host tests that are out of the scope of this article.

The dto folder holds “data transfer objects” for each request. The index file is used to export all dto-s.