Let's learn Node.js by building a backend with Nest.js and Sequelize - Lesson 2: User registration PART 1
Tags: node, javascript, postgres, beginners
Published: 2020-06-04 15:31:37

This article was originally published at https://www.blog.duomly.com/node-js-course-with-building-a-fintech-banking-app-lesson-2-user-registration

---

In the previous week, I've published <a href="https://www.blog.duomly.com/node-js-course-with-building-a-fintech-banking-app-lesson-1-start-the-project/">the first lesson of the Node.js Course,</a> where we started a project using Nest.js, Nest CLI, PostgreSQL database, and Sequelize. Besides that, we managed to create migrations and set up the database.

So, if you would like to be updated, feel free to go back to the lesson one and follow up or get the first lesson code from our <a href="https://github.com/Duomly/nodejs-backend-bankapp/tree/Nodejs-Course-Lesson1">Github.</a>

Also, if you would like to compare backend created in Node.js with backend created with GoLang, then check out my friend's <a href="https://www.blog.duomly.com/golang-course-with-building-a-fintech-banking-app-lesson-1-start-the-project/">Golang Course</a>. Both are created along with the <a href="https://www.blog.duomly.com/angular-course-building-a-banking-application-with-tailwind-css-lesson-1-start-the-project/">Angular 9 Course</a> and <a href="https://www.blog.duomly.com/python-course-with-building-a-fintech-investment-ai-lesson-1-start-the-project/">Python and AI Course</a>. All of them are used to build one fintech application.

Today I'm going to show you how to create user registration in Node.js.
We are going to create two modules, Users and Accounts, and we will build the functionality for creating a new user, and each new user will have a new account assigned.

And of course, as always, we've got a video version for you!

{% youtube ZArYOJNMnuk %}

Let's start!

###1. Refactor migrations###

The first step will be to refactor the migrations we did in the last lesson. We have to add a few columns to the tables. So, let's run `npm run migrate down` twice to drop both tables. Open `1.1users.ts` file and make the following changes in the code.

```typescript
import * as Sequelize from 'sequelize';

const tableName = 'Users';

export async function up(i: any) {
  const queryInterface = i.getQueryInterface() as Sequelize.QueryInterface;
  queryInterface.createTable(tableName, {
    id: {
      type: Sequelize.INTEGER,
      allowNull: false,
      autoIncrement: true,
      unique: true,
      primaryKey: true,
    },
    Username: {
      type: Sequelize.CHAR(200),
      allowNull: false,
    },
    Email: {
      type: Sequelize.CHAR(50),
      allowNull: false,
    },
    Password: {
      type: Sequelize.CHAR(250),
      allowNull: false,
    },
    Salt: {
      type: Sequelize.CHAR(250),
      allowNull: true,
    },
    createdAt: {
      type: Sequelize.DATE,
    },
    updatedAt: {
      type: Sequelize.DATE,
    }
  });
};

export async function down(i: any) {
  const queryInterface = i.getQueryInterface() as Sequelize.QueryInterface;
  queryInterface.dropTable(tableName);
}
```
Now, open the other migrations file `1.2accounts.ts` and make sure it looks like in the code below.

```typescript
import * as Sequelize from 'sequelize';

const tableName = 'Accounts';

export async function up(i: any) {
  const queryInterface = i.getQueryInterface() as Sequelize.QueryInterface;
  queryInterface.createTable(tableName, {
    id: {
      type: Sequelize.INTEGER,
      allowNull: false,
      autoIncrement: true,
      unique: true,
      primaryKey: true,
    },
    Type: {
      type: Sequelize.CHAR(200),
      allowNull: false,
    },
    Name: {
      type: Sequelize.CHAR(200),
      allowNull: false,
    },
    Balance: {
      type: Sequelize.INTEGER,
      allowNull: true,
    },
    UserId: {
      type: Sequelize.INTEGER,
      references: {
        model: 'Users',
        key: 'id',
      },
    },
    createdAt: {
      type: Sequelize.DATE,
    },
    updatedAt: {
      type: Sequelize.DATE,
    }
  });
};

export async function down(i: any) {
  const queryInterface = i.getQueryInterface() as Sequelize.QueryInterface;
  queryInterface.dropTable(tableName);
}
```

The last step now is to rerun migrations, so let's use `npm run migrate up` and check if your database changed.

###2. Install packages###

To create user registration, we need some additional packages. Let's open the console and install `jsonwebtoken`.

```bash
$ npm install jsonwebtoken
```
And the other package we need is `dotenv`, to create the configuration.

```bash
$ npm install dotenv
```
When it's done, let's go to the next step.

###3. Create .env file###

Go to the root file, create a new file, and call it `.env`. To this file, we are going to move database configuration. 

```
DB_HOST=<YOUR_HOST>
DB_USER=<YOUR_USERNAME>
DB_PASS=<YOUR_PASSWORD>
DB_NAME=<YOUR_DB_NAME>
JWT_KEY=<YOUR_JWT_KEY>
```
Now, we need to require this configuration in the `main.ts` file, and later, let's change the database configuration in the `migrate.ts` and `database.provider.ts` files.

Let's start from `main.ts` and import the `.env`.
```typescript
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
require('dotenv').config()

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  await app.listen(3000);
}
bootstrap();
```

Now, open `migrate.ts` and make sure it looks like this.

```typescript
...
require('dotenv').config()

const sequelize = new Sequelize({
  dialect: 'postgres',
  host: process.env.DB_HOST,
  port: 5432,
  username: process.env.DB_USER,
  password: process.env.DB_PASS,
  database: process.env.DB_NAME,
});
...
```

And finally, open the `database.provider.ts` file.

```typescript
export const databaseProvider = [
  {
    provide: 'SEQUELIZE',
    useFactory: async () => {
      const sequelize = new Sequelize({
        dialect: 'postgres',
        host: process.env.DB_HOST,
        port: 5432,
        username: process.env.DB_USER,
        password: process.env.DB_PASS,
        database: process.env.DB_NAME,
      });
      sequelize.addModels([Users, Accounts]);
      return sequelize;
    }
  }
]
```
In the next step, we will create a JWT config.

###4. JWT config###

Let's go to the `src` folder and create a new folder called `config`. In that config folder, we will create a `jwtConfig.ts` file, and let's set options for our jwt.

```typescript
export const jwtConfig = {
  algorithm: 'HS256',
  expiresIn: '1 day',
}
```

Another necessary thing, we need to do right now is to generate a JWT_KEY and add it to the `.env`.
You can generate the key using any of the tools available on the internet or using the console command: 
```
ssh-keygen -t rsa -b 2048 -f jwtRS256.key
```

When it's ready and added to the `.env` file, let's got to the next step!

###5. User module and entity###

In this step, we are going to create a `user.module.ts` and we can do it manually or using Nest CLI.

```
$ nest generate module modules/user
```

Right now, we don't need to do anything else in this file, so let's create the next file in the user folder, `users.entity.ts`. And inside this file, we are going to set the data we will pass to the database.

```typescript
import { Table, Column, Model, DataType, CreatedAt, UpdatedAt, HasMany } from 'sequelize-typescript';
import { TableOptions } from 'sequelize-typescript';

const tableOptions: TableOptions = { timestamp: true, tableName: 'Users' } as TableOptions;

@Table(tableOptions)
export class Users extends Model<Users> {
  @Column({
    type: DataType.INTEGER,
    allowNull: false,
    autoIncrement: true,
    unique: true,
    primaryKey: true,
  })
  public id: number;

  @Column({
      type: DataType.CHAR(200),
      allowNull: false,
  })
  public Username: string;

  @Column({
    type: DataType.CHAR(50),
    allowNull: false,
    validate: {
      isEmail: true,
      isUnique: async (value: string, next: Function): Promise<any> => {
        const exists = await Users.findOne({ where: { Email: value } });
        if (exists) {
          const error = new Error('This email is already used.');
          next(error);
        }
        next();
      }
    }
  })
  public Email: string;

  @Column({
    type: DataType.CHAR(250),
    allowNull: false,
  })
  public Password: string;

  @Column({
    type: DataType.CHAR(250),
    allowNull: true,
  })
  public Salt: string;

  @CreatedAt
  public createdAt: Date;

  @UpdatedAt
  public updatedAt: Date;
}
``` 

Great, now we can go to the next point!

###6. User provider and interface###

Next, what we will do now is to create a user provider. Inside the user folder create `users.provider.ts` file, and inside that file, let's create the following code.

```typescript
import { Users } from './users.entity';

export const UsersProviders = {
  provide: 'USERS_REPOSITORY',
  useValue: Users
}
```
When it's done, let's open the module and add the provider there.

```typescript
@Module({
  providers: [UsersProviders],
  exports: [
    UsersProviders,
  ]
})
```

Now let's create the interface, where we will define the types of the User object.
In the `user` folder, create a new folder `interface` and in that folder, create `user.interface.ts` file. In this file, create the following interface.

```typescript
export interface IUser {
  id: number;
  Username: string;
  Email: string;
  Password: string;
  Salt: string;
  Accounts: [];
}
```

Cool, now we can go to the most exciting part of this lesson, ready?

###7. User service and controller###

At this point, we will create a `user.service.ts` file, and inside this file, we will build the function which will be saving data to the database.

Open the newly created file and type the following code.

```typescript
import { Injectable, Inject } from '@nestjs/common';
import { Users } from './users.entity';
import * as jwt from 'jsonwebtoken';
import { jwtConfig } from './../../config/jwtConfig';
import crypto = require('crypto');

@Injectable()
export class UsersService { 
  constructor(
    @Inject('USERS_REPOSITORY') private usersRepository: typeof Users,
  ) { }

  public async create(user: any): Promise<object> {
    const exists = await Users.findOne({ where: { Email: user.Email } });
    if (exists) {
      throw new Error('This email is already used.');
    } else {
      user.Salt = crypto.randomBytes(128).toString('base64');
      user.Password = crypto.createHmac('sha256', user.Password + user.Salt).digest('hex');
      const newUser: any = await this.usersRepository.create<Users>(user);
      const jwtToken = jwt.sign(user, process.env.JWT_KEY, jwtConfig);
      newUser.Token = jwtToken;
      return newUser;
    }
  }
}
```
Great, it looks like that's it! We just need a controller right now, where we will set the endpoint and API method.

Let's create a `user.controller.ts` file and create the following code.

```typescript
import { Controller, Post, Body, HttpException, HttpStatus } from '@nestjs/common';
import { UsersService } from './users.service';
import { IUser } from './interfaces/user.interface';

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

  @Post('register')  
    public async register(@Body() user: IUser): Promise<any> {    
    const result: any = await this.usersService.create(user,);
    if (!result.success) {
        throw new HttpException(result.message, HttpStatus.BAD_REQUEST);    
    }
    return result;  
  }
}
``` 

Now, we need to inject those file to the module file.

```typescript
@Module({
  controllers: [UsersController],
  providers: [UsersProviders, UsersService],
  exports: [
    UsersService,
    UsersProviders,
  ]
})
```
It seems like the first part of our registration is ready, so let's create the second part, where we will create an account for each registered user.

###8. Accounts module and entity###

Let's use the Nest CLI and create a new module.

```bash
$ nest generate module modules/accounts
```
You should see the new module created in a new folder, and as previously, we have nothing to do in the accounts module file right now. So, let's create the `accounts.entity.ts` file and make sure your file looks like the code below.

```typescript
import { Table, Column, Model, DataType, CreatedAt, UpdatedAt, ForeignKey, BelongsTo } from 'sequelize-typescript';
import { TableOptions } from 'sequelize-typescript';
import { Users } from '../user/users.entity';

const tableOptions: TableOptions = { timestamp: true, tableName: 'Accounts' } as TableOptions;
@Table(tableOptions)
export class Accounts extends Model<Accounts> {
  @Column({
    type: DataType.INTEGER,
    allowNull: false,
    autoIncrement: true,
    unique: true,
    primaryKey: true,
  })
  public id: number;

  @Column({
    type: DataType.CHAR(200),
    allowNull: false,
  })
  public Type: string;

  @Column({
    type: DataType.CHAR(200),
    allowNull: false,
  })
  public Name: string;

  @Column({
    type: DataType.INTEGER,
    allowNull: true,
  })
  public Balance: number;

  @ForeignKey(() => Users)
  public UserId: number;

  @BelongsTo(() => Users, {
      as: 'Users',
      foreignKey: 'UserId',
      targetKey: 'id',
  })
  public Users: Users;

  @CreatedAt
  public createdAt: Date;

  @UpdatedAt
  public updatedAt: Date;
}
```

Great, it's ready, so we can go to the next steps.

###9. Accounts provider and interface###

Let's create a provider for the accounts module right now. In the `accounts` folder, please create the `accounts.provider.ts` file. In this file, you need to set the provider with the following code.

```typescript
import { Accounts } from './accounts.entity';

export const AccountsProviders = {
  provide: 'ACCOUNTS_REPOSITORY',
  useValue: Accounts
};
```
As previously we need an interface, so let's create the new folder called `interfaces` and inside that file create the `accounts.interface.ts` file with the following object inside.

```typescript
export interface IAccount {
  id: number;
  Type: string;
  Name: string;
  Balance: number;
  UserId: number;
}
```

We are ready to create AccountsService and controller. 

###10. Accounts service and controller###

In the `accounts` folder, let's create an `accounts.service.ts` file, and in this file, you need to create the following function.

```typescript
import { Injectable, Inject } from '@nestjs/common';
import { Accounts } from './accounts.entity';

@Injectable()
export class AccountsService { 
  constructor(
    @Inject('ACCOUNTS_REPOSITORY')
    private accountsRepository: typeof Accounts
  ) { }
  
  public async create(UserId: number): Promise<object> {
    const account = {
      Name: 'Account',
      Type: 'Personal Account',
      Balance: 100, 
      UserId: UserId,
    }
    const newAccount: any = await this.accountsRepository.create<Accounts>(account);
    return newAccount;
  }
}
```
As you can see, we are setting the hardcoded values for our initial user account as it will be the default one, and later user will be able to make changes.

Let's create an `accounts.controller.ts` file in the same folder. And in that file type the following code, so we will be able to use it from the endpoint as well.

```typescript
import { AccountsService } from './accounts.service';
import { Controller, Post, Body, HttpException, HttpStatus } from '@nestjs/common';
import { IAccount } from './interfaces/accounts.interface';

@Controller('accounts')
export class AccountsController {
  constructor(private accountsService: AccountsService) { }

  @Post('create-account')  
    public async register(@Body() UserId: number): Promise<any> {    
    const result: any = await this.accountsService.create(UserId);
    if (!result.success) {
      throw new HttpException(result.message, HttpStatus.BAD_REQUEST);    
    }
    return result;  
  }
}
```

We are almost at the end of the lesson. We just need to update a few files and test it.

###11. Add AccountsService to UserService###

We will be using the function to create an account in our register function. But first, we need to update modules, so let's open `accounts.module.ts` file and make sure it looks like the code below.

```typescript
@Module({
  imports: [DatabaseModule],
  controllers: [AccountsController],
  providers: [AccountsProviders, AccountsService],
  exports: [AccountsProviders, AccountsService]
})
``` 
When it's saved, open the other module file `users.module.ts` and update it as well.

```typescript
@Module({
  controllers: [UsersController],
  imports: [AccountsModule],
  providers: [UsersProviders, UsersService],
  exports: [
    UsersService,
    UsersProviders,
  ]
})
```
So, we can import it in the `user.service.ts`. The file should look like the code below now.

```typescript
import { Injectable, Inject } from '@nestjs/common';
import { Users } from './users.entity';
import * as jwt from 'jsonwebtoken';
import { jwtConfig } from './../../config/jwtConfig';
import { AccountsService } from './../accounts/accounts.service';
import crypto = require('crypto');

@Injectable()
export class UsersService { 
  constructor(
    @Inject('USERS_REPOSITORY') private usersRepository: typeof Users,
    private accountsService: AccountsService,
  ) { }

  public async create(user: any): Promise<object> {
    const exists = await Users.findOne({ where: { Email: user.Email } });
    if (exists) {
      throw new Error('This email is already used.');
    } else {
      user.Salt = crypto.randomBytes(128).toString('base64');
      user.Password = crypto.createHmac('sha256', user.Password + user.S
alt).digest('hex');
      const newUser: any = await this.usersRepository.create<Users>(user);
      const jwtToken = jwt.sign(user, process.env.JWT_KEY, jwtConfig);
      newUser.Token = jwtToken;
      if (newUser) {
        this.accountsService.create(newUser.id)
      }
      return newUser;
    }
  }
}
```

Ok, we've passed the create function from accountsService and created a new account each time the new user registers.

To make the whole backend working, we need to update the user entity and database provider. Let's open the user.entity.ts file, and we will add a few lines of code at the end of the User class.

```typescript
  @HasMany(() => Accounts, 'UserId')
  public Accounts: Accounts[];
```

Now, let's open database.provider.ts file and import both entities. When the import is done, inject them as models.

```typescript
import { Sequelize } from 'sequelize-typescript'; 
import { Users } from '../user/users.entity'; 
import { Accounts } from '../accounts/accounts.entity'; 
export const databaseProvider = [ 
  { 
      provide: 'SEQUELIZE', 
      useFactory: async () => { 
         const sequelize = new Sequelize({ 
            dialect: 'postgres', 
            host: process.env.DB_HOST, 
            port: 5432, 
            username: process.env.DB_USER, 
            password: process.env.DB_PASS, 
            database: process.env.DB_NAME, 
         }); 
     sequelize.addModels([Users, Accounts]); 
     return sequelize; 
    } 
  }
]
```

And voila! Let's test it!

###12. Testing###

I'll be using the Postman to test our API right now. If you are not running the app yet, please do it using `nest start` or `npm run start`, and when it's ready to open the Postman. In the image below, you can see my setting, so you can try similar. Also, you can open your database to see if the data is there.

![Duomly - Node.js Course](https://dev-to-uploads.s3.amazonaws.com/i/yqrzlyn4yyudv4xlpcpp.png)

I hope it works for you as well!

###Conclusion###

In this lesson, we build the registration of a new user and creating the default account. 
In the next lessons, we are going to work on the login feature.
If you didn't manage to get all the code correctly, jump into our Github and find your bugs.

<a href="https://github.com/Duomly/nodejs-backend-bankapp/tree/Nodejs-Course-Lesson-2">Node.js Course - Lesson 2: User registration - Code</a>

Also, remember to jump into other courses where we are building <a href="https://www.blog.duomly.com/golang-course-with-building-a-fintech-banking-app-lesson-1-start-the-project/">GoLang backend</a> for the same app, <a href="https://www.blog.duomly.com/python-course-with-building-a-fintech-investment-ai-lesson-1-start-the-project/">AI for investment</a>, and frontend with <a href="https://www.blog.duomly.com/angular-course-building-a-banking-application-with-tailwind-css-lesson-1-start-the-project/">Angular 9</a>.

Thank you for reading,
Anna from Duomly

<a href="https://www.duomly.com">
![Duomly - Programming Online Courses](https://dev-to-uploads.s3.amazonaws.com/i/y13s6omwc7ewb078cewi.jpg)
</a>