This is the first installment of the NetsJS JWT Auth Cookie Series. In this part our main focus on user registration by the NestJS endpoint.
Open Postgres Docker interactive terminal and query the recently registered user record.
So that's all about the NetstJS user registration. In the next part, we will create a jwt auth cookie.
PostgreSQL Database:
For this demo, I'm using the free open-source PostgreSQL database. Here I'm going to use the PostgreSQL docker image because it is easy and fast to set up and configure. Click here to getting started with PostgreSQL docker.
Run the following database query to create the 'User' table.
CREATE TABLE User( UserId SERIAL PRIMARY KEY NOT NULL, FirstName VARCHAR(200) NULL, LastName VARCHAR(200) NULL, Email VARCHAR(200) NOT NULL, Password VARCHAR(200) NOT NULL, RefreshToken VARCHAR(1000) NULL, RefreshTokenExp DATE NULL )
Create A NestJS App:
Let's begin our demo by creating a sample NestJS application.
Command To Install NestJS CLI
npm i -g @nestjs/cli
Command To Create App
nest new your_project_name
Install ORM And PostgreSQL NPM Packages:
ORM packages are essential to install because they provide some boilerplate functional mechanism that builds the bridge between our application and database for communication.
Install NestJS Type ORM
npm install --save @nestjs/typeorm
Install NodeJS Type ORM
npm install --save typeorm
Install PostgreSQL node library.
Install NodeJS PostgreSQL
npm install --save pg
Configure PostgreSQL Settings:
Here I'm running my PostgreSQL using docker. Now we have to set up database configuration into our application to establish communication.
src/app.module.ts:
import { TypeOrmModule } from '@nestjs/typeorm'; // code hidden for display purpose @Module({ imports: [ TypeOrmModule.forRoot({ type:'postgres', host:'localhost', port: 5432, username: 'postgres', password:'secret', database:'myworlddb', entities:[] }) ] }) export class AppModule {}
- The 'TypeOrmModule' is a generic module that can be easily compatible with most database configurations.
- (Line: 6) The 'type' property to define the database. So here we mentioned our database is 'postgres'.
- (Line: 7) The 'host' property to define the database host. Since our demo using docker instance, so our value will be 'localhost'.
- (Line: 8) The default port number Postgres runs is 5432. Here I used the same port to expose from my docker instance.
- (Line: 9-10) Database credentials like 'username' and 'password'. (Note: Since here I'm using docker, these values can be given when running the docker container. If you don't specify the user name then the default username will be 'postgres').
- (Line: 11) Define the name of the database.
- (Line: 12) All table classes need to be defined in the 'entities' array.
Create User Files:
Now we have to create all user-related files like 'module', 'service', 'controller', 'entity'.
Command To Create Module File
nest g mo Users
Command To Create Service File
nest g s Users --no-spec
Command To Create Controller File
nest g co Users --no-spec
Command To Create Class File
nest g cl Users/user --no-spec
Note: Remove the 'UsersController' from 'AppModule' and register the 'UsersController' in 'UsersModule'.
User Table Entity:
Now let's update our 'src/users/user.ts' file with all table relate columns as properties.
src/users/user.ts:
import { Column, Entity, PrimaryColumn, PrimaryGeneratedColumn } from 'typeorm'; @Entity({name:'users'}) export class User { @PrimaryGeneratedColumn('increment',{name:'userid'}) userId: number; @Column({name:'firstname'}) firstName: string; @Column({name:'lastname'}) lastName: string; @Column({name:'email'}) email: string; @Column() password: string; @Column({ nullable: true,name:'refreshtoken' }) refreshToken: string; @Column({ type: 'date', nullable: true, name:'refreshtokenexp' }) refreshTokenExp: string; }Now register our 'User' entity in 'users.module.ts' and 'app.module.ts'.
src/users/users.modulet.ts:
import { TypeOrmModule } from '@nestjs/typeorm'; import { User } from './user'; // code hidden for disply purpose @Module({ imports: [TypeOrmModule.forFeature([User])], providers: [UsersService], }) export class UsersModule {}src/app.module.ts:
import { TypeOrmModule } from '@nestjs/typeorm'; import { User } from './users/user'; // code hidden for display purpose @Module({ imports: [ TypeOrmModule.forRoot({ entities:[User] }) ] }) export class AppModule {}
Install BCRYPT NPM Package:
For password hashing, we will use 'bcrypt' node package.
Command For bcrypt Node Package
npm i bcrypt
Command For bcrypt Typescript Package
npm i -D @types/bcrypt
Implement Registration Logic:
Now let's create request and response models for the registration endpoint.
src/models/registration.req.model.ts:
export class RegistrationReqModel { firstName: string; lastName: string; email: string; password: string; confirmPassword: string; }src/models/registration.resp.model.ts:
export class RegistrationRespModel { successStatus: boolean; message: string; }Inject the 'User' repository instance into the 'UserService'.
src/users/users.service.ts:
import { Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { RegistrationReqModel } from 'src/models/registration.req.model'; import { RegistrationRespModel } from 'src/models/registration.resp.model'; import { Repository } from 'typeorm'; import { User } from './user'; import * as bcrypt from 'bcrypt'; @Injectable() export class UsersService { constructor(@InjectRepository(User) private user: Repository<User>) {} }Add logic to validate the registration payload before creating the new user.
src/users/users.service.ts:
private async registrationValidation(regModel: RegistrationReqModel): Promise<string> { if (!regModel.email) { return "Email can't be empty"; } const emailRule = /^(([^<>()[\]\.,;:\s@\"]+(\.[^<>()[\]\.,;:\s@\"]+)*)|(\".+\"))@(([^<>()[\]\.,;:\s@\"]+\.)+[^<>()[\]\.,;:\s@\"]{2,})$/i; if (!emailRule.test(regModel.email.toLowerCase())) { return 'Invalid email'; } const user = await this.user.findOne({ email: regModel.email }); if (user != null && user.email) { return 'Email already exist'; } if (regModel.password !== regModel.confirmPassword) { return 'Confirm password not matching'; } return ''; }
- Here we implemented few rules like email validation, email already exists or not, comparing password value with confirm password.
src/users/users.service.ts:
private async getPasswordHash(password: string): Promise<string> { const hash = await bcrypt.hash(password, 10); return hash; }Now let's create our entry method of our user registration.
src/users/users.service.ts:
public async registerUser( regModel: RegistrationReqModel, ): Promise<RegistrationRespModel> { let result = new RegistrationRespModel(); const errorMessage = await this.registrationValidation(regModel); if (errorMessage) { result.message = errorMessage; result.successStatus = false; return result; } let newUser = new User(); newUser.firstName = regModel.firstName; newUser.lastName = regModel.lastName; newUser.email = regModel.email; newUser.password = await this.getPasswordHash(regModel.password); await this.user.insert(newUser); result.successStatus = true; result.message = 'success'; return result; }Let's create our registration endpoint.
src/users/users.controller.ts:
import { Body, Controller, Post } from '@nestjs/common'; import { RegistrationReqModel } from 'src/models/registration.req.model'; import { UsersService } from './users.service'; @Controller('users') export class UsersController { constructor(private userService:UsersService){} @Post('registration') async registerUser(@Body() reg: RegistrationReqModel){ return await this.userService.registerUser(reg); } }Now test the registration endpoint.
Open Postgres Docker interactive terminal and query the recently registered user record.
Video Session:
Support Me!
Buy Me A Coffee
PayPal Me
Wrapping Up:
Hopefully, I think this article delivered some useful information on NestJS user registration. I love to have your feedback, suggestions, and better techniques in the comment section below.
Comments
Post a Comment