---
title: "Setting up JWT Authentication in TypeScript with Express, MongoDB, Babel, Prettier, ESLint, and Husky - Part 2"
lang: "en"
author: "Mohammad Abu Mattar"
canonical: https://mkabumattar.com/post/setting-up-jwt-authentication-in-typescript-with-express-mongodb-babel-prettier-eslint-and-husky-part-2
---

![Blog post image for Setting up JWT Authentication in TypeScript with Express, MongoDB, Babel, Prettier, ESLint, and Husky - Part 2 - Setting up JWT Authentication in Typescript with Express, MongoDB, Babel, Prettier, ESLint, and Husky: Part 2.](/_astro/hero.DKzl3k6w_ZgGokB.webp)

[Home](/)›[Blog](/blog)›[All Categories](/blog/categories)›[Backend Development](/blog/categories/backend-development)

Blog

[Prev in Backend DevelopmentREST API vs RESTful API: Architecture and Constraints Explained](/blog/post/rest-api-vs-restful-api-architecture-and-constraints-explained)[Next in Backend DevelopmentSetting up Node JS, Express, MongoDB, Prettier, ESLint and Husky Application with Babel and authentication as an example](/blog/post/setting-up-node-js-express-mongodb-prettier-eslint-and-husky-application-with-babel-and-authentication-as-an-example)

[Backend Development](/blog/categories/backend-development)[Node.js](/blog/categories/nodejs)[TypeScript](/blog/categories/typescript)[Authentication](/blog/categories/authentication)[API Development](/blog/categories/api-development)

# Setting up JWT Authentication in TypeScript with Express, MongoDB, Babel, Prettier, ESLint, and Husky - Part 2

[Mohammad Abu Mattar](/authors/mohammad-abu-mattar)Published: 03 Jul 202219 Mins read10 Mins listen

[Markdown for AI(opens in a new tab)](/post/setting-up-jwt-authentication-in-typescript-with-express-mongodb-babel-prettier-eslint-and-husky-part-2/index.md "Open the plain-Markdown version of this page, for pasting into an AI tool")

TL;DR

Setting up JWT Authentication in Typescript with Express, MongoDB, Babel, Prettier, ESLint, and Husky: Part 2.

Series

[Node.js Express TypeScript Setup](/series/nodejs-express-typescript-setup)2/2

[PreviousSetting up Node.js, Express, Prettier, ESLint, and Husky application with Babel and TypeScript - Part 1](/blog/post/setting-up-node-js-express-prettier-eslint-and-husky-application-with-babel-and-typescript-part-1)

All posts in this series (2)

Blog2

1.  [Setting up Node.js, Express, Prettier, ESLint, and Husky application with Babel and TypeScript - Part 1](/blog/post/setting-up-node-js-express-prettier-eslint-and-husky-application-with-babel-and-typescript-part-1)
2.  [Setting up JWT Authentication in TypeScript with Express, MongoDB, Babel, Prettier, ESLint, and Husky - Part 2You are here](/blog/post/setting-up-jwt-authentication-in-typescript-with-express-mongodb-babel-prettier-eslint-and-husky-part-2)

### Setting up JWT Authentication in TypeScript with Express, MongoDB, Babel, Prettier, ESLint, and Husky - Part 2

Contents

[Introduction](#introduction)[Directory and file structure](#directory-and-file-structure)[Environment variables](#environment-variables)[Setup logger for development](#setup-logger-for-development)[Setup MongoDB using Mongoose](#setup-mongodb-using-mongoose)[Setup validation using Joi](#setup-validation-using-joi)[Setup JWT authentication](#setup-jwt-authentication)[Summary](#summary)[References](#references)

## [Introduction](#introduction)

Why do we even need an authentication mechanism in an application? In my opinion, it doesn’t need to be explained. The phrases authentication and authorization have likely crossed your lips, but I must emphasize that they have two distinct meanings.

-   Authentication: Any security approach must start with authentication, verifying that users are who they claim to be.
-   Authorization: Authorization in the context of system security describes the procedure for authorizing user access to a certain resource or function. The words “access control” and “client privilege” are commonly used interchangeably.

At the same time, the words “authentication” and “authorization” are used in the context of network security. In this context, authentication is the process of verifying that a user is who they claim to be. Authorization is the process of verifying that a user has the necessary rights to access a certain resource or function.

We will learn how to create an authentication system using JWT in this tutorial. We will also learn how to create an authorization system using JWT with Typescript and Express. The tutorial is a continuation of the previous tutorial.

-   [Setting up Node JS, Express, Prettier, ESLint and Husky Application with Babel and Typescript: Part 1](/post/setting-up-node-js-express-prettier-eslint-and-husky-application-with-babel-and-typescript-part-1)

## [Directory and file structure](#directory-and-file-structure)

We’ll start by creating a directory structure for our application, and then we’ll create a file structure for our application.

```
1├── src2│   ├── bin3│   │   └── www.ts4│   ├── config5│   │   └── db.config.ts6│   ├── constants7│   │   ├── api.constant.ts8│   │   ├── dateformat.constant.ts9│   │   ├── http.code.constant.ts10│   │   ├── http.reason.constant.ts11│   │   ├── message.constant.ts12│   │   ├── model.constant.ts13│   │   ├── number.constant.ts14│   │   ├── path.constant.ts15│   │   └── regex.constant.ts16│   ├── controllers17│   │   ├── auth.controller.ts18│   │   └── user.controller.ts19│   ├── env20│   │   └── variable.env.ts21│   ├── interfaces22│   │   ├── controller.interface.ts23│   │   └── user.interface.ts24│   ├── middlewares25│   │   ├── authenticated.middleware.ts26│   │   ├── error.middleware.ts27│   │   └── validation.middleware.ts28│   ├── models29│   │   └── user.model.ts30│   ├── repositories31│   │   └── user.repository.ts32│   ├── schemas33│   │   └── user.schema.ts34│   ├── security35│   │   └── user.security.ts36│   ├── services37│   │   ├── auth.service.ts38│   │   └── user.service.ts39│   ├── types40│   │   └── express41│   │       └── index.d.ts42│   ├── utils43│   │   └── exceptions44│   │   │   └── http.exception.ts45│   │   └── logger.util.ts46│   └── validations47│       ├── token.validation.ts48│       ├── user.validation.ts49│       └── variable.validation.ts50├── .babelrc51├── .env52├── .env.example53├── .eslintignore54├── .eslintrc55├── .gitattributes56├── .gitignore57├── .npmrc58├── .nvmrc59├── .prettierignore60├── .prettierrc61├── commitlint.config.js62├── package.json63├── README.md64├── tsconfig.json65└── yarn.lock
```

Don’t be overwhelmed; this structure will be helpful after the program is finished and you start expanding the file structure for the business logic. This is just my opinion; perhaps you’ll organize the directory and files differently.

We’ll be continuing build-up in the last tutorial [repository](https://github.com/MKAbuMattar/template-express-typescript-blueprint/tree/part1).

To better arrange the file structure and identify the key files, certain adjustments will be made to `tsconfig.json`.

`tsconfig.json`

```
1{2  ...,3  "paths": {4    "@/bin/*": [5      "bin/*"6    ],7    "@/config/*": [8      "config/*"9    ],10    "@/constants/*": [11      "constants/*"12    ],13    "@/controllers/*": [14      "controllers/*"15    ],16    "@/env/*": [17      "env/*"18    ],19    "@/interfaces/*": [20      "interfaces/*"21    ],22    "@/middlewares/*": [23      "middlewares/*"24    ],25    "@/models/*": [26      "models/*"27    ],28    "@/repositories/*": [29      "repositories/*"30    ],31    "@/routers/*": [32      "routers/*"33    ],34    "@/schemas/*": [35      "schemas/*"36    ],37    "@/security/*": [38      "security/*"39    ],40    "@/services/*": [41      "services/*"42    ],43    "@/utils/*": [44      "utils/*"45    ],46    "@/validations/*": [47      "validations/*"48    ],49  },50}
```

nevertheless, to use the file structure, we must install a package called `module-alias`. To install the package, use the following command after generating the project:

Terminal window

```
1yarn add module-alias
```

Terminal window

```
1yarn add -D @types/module-alias
```

and we need to do some change to `package.json` and add `_moduleAliases`:

```
1{2  ...,3    "_moduleAliases": {4    "@/bin": "build/bin",5    "@/config": "build/config",6    "@/constants": "build/constants",7    "@/controllers": "build/controllers",8    "@/env": "build/env",9    "@/interfaces": "build/interfaces",10    "@/middlewares": "build/middlewares",11    "@/models": "build/models",12    "@/repositories": "build/repositories",13    "@/routers": "build/routers",14    "@/schemas": "build/schemas",15    "@/security": "build/security",16    "@/services": "build/services",17    "@/utils": "build/utils",18    "@/validations": "build/validations"19  }20}
```

## [Environment variables](#environment-variables)

A basic text configuration file called a `.env` file or `dotenv` file is used to manage the environment constants for your applications. The vast bulk of your application will remain the same throughout the Local, Staging, and Production environments. However, there are times when some configurations need to be changed between environments in various applications. Typical setup adjustments across contexts might be, but are not restricted to:

-   URLs and API keys
-   Domain names
-   Public and private authentication keys
-   Service account names

An environment constant is a variable whose value is set outside the application, generally via operating system capability. Any number of environment variables may be generated and made accessible for use at one time; each environment variable consists of a name/value pair.

After creating the directory structure, we’ll create a file called `.env` and `.env.example` in the root directory:

-   `.env`: This file will contain the configuration for the application.
-   `.env.example`: is the file that contains all of the configurations for constants that `.env` has, but without values; only this one is versioned. `env.example` serves as a template for building a `.env` file that contains the information required to start the program.

Terminal window

```
1touch .env .env.example
```

Now we add a new variable to `.env`:

`.env`

Terminal window

```
1NODE_ENV=development2# NODE_ENV=production3PORT=30304DATABASE_URL=mongodb://127.0.0.1:27017/example5
6JWT_SECRET=secret7PASS_SECRET=secret
```

`.env.example`

Terminal window

```
1NODE_ENV=development2# NODE_ENV=production3PORT=30304DATABASE_URL=mongodb://5
6JWT_SECRET=secret7PASS_SECRET=secret
```

They will now be loaded and used using the library `dotenv`, and environment variables will be verified by a different library called `envalid`.

Terminal window

```
1yarn add dotenv envalid
```

`variable.validation.ts`

```
1import {cleanEnv, str, port} from 'envalid';2
3const validate = (): void => {4  cleanEnv(process.env, {5    NODE_ENV: str({6      choices: ['development', 'production'],7    }),8    PORT: port({default: 3030}),9    DATABASE_URL: str(),10    JWT_SECRET: str(),11    PASS_SECRET: str(),12  });13};14
15export default validate;
```

`variable.env.ts`

```
1import VariableValidate from '@/validations/variable.validation';2import 'dotenv/config';3
4class Variable {5  public static readonly NODE_ENV: string = process.env.NODE_ENV!;6
7  public static readonly PORT: number = Number(process.env.PORT)!;8
9  public static readonly DATABASE_URL: string = process.env.DATABASE_URL!;10
11  public static readonly JWT_SECRET: string = process.env.JWT_SECRET!;12
13  public static readonly PASS_SECRET: string = process.env.PASS_SECRET!;14
15  constructor() {16    this.initialise();17  }18
19  private initialise(): void {20    VariableValidate();21  }22}23
24export default Variable;
```

## [Setup logger for development](#setup-logger-for-development)

I had a problem setting up the logger when constructing a server-side application based on Node and Express router. Conditions for the answer:

-   Logging application event
-   Ability to specify multiple logs level
-   Logging of HTTP requests
-   Ability to write logs into a different source (console and file)

I found two possible solutions:

-   [Morgan](https://www.npmjs.com/package/morgan): HTTP logging middleware for express. It provides the ability to log incoming requests by specifying the formatting of log instance based on different request related information.
-   [Winston](https://www.npmjs.com/package/winston): Multiple types of transports are supported by a lightweight yet effective logging library. I need this because I want to log events into a file and a terminal at the same time.

I’ll use Winston for the logging, first I’ll install the package:

Terminal window

```
1yarn add winston
```

We’ll begin by introducing the constants, which will be applied as follows:

`dateformat.constant.ts`

```
1class Dateformat {2  public static readonly YYYY_MM_DD_HH_MM_SS_MS: string =3    'YYYY-MM-DD HH:mm:ss:ms';4}5
6export default Dateformat;
```

`path.constant.ts`

```
1class Path {2  public static readonly LOGS_ALL: string = 'logs/all.log';3
4  public static readonly LOGS_ERROR: string = 'logs/error.log';5}6export default Path;
```

We’ll now create the `winston` as a function to make it simpler to use:

`logger.util.ts`

```
1import ConstantDateFormat from '@/constants/dateformat.constant';2import ConstantPath from '@/constants/path.constant';3import Variable from '@/env/variable.env';4import winston from 'winston';5
6const levels = {7  error: 0,8  warn: 1,9  info: 2,10  http: 3,11  debug: 4,12};13
14const level = () => {15  const env = Variable.NODE_ENV || 'development';16  const isDevelopment = env === 'development';17  return isDevelopment ? 'debug' : 'warn';18};19
20const colors = {21  error: 'red',22  warn: 'yellow',23  info: 'green',24  http: 'magenta',25  debug: 'white',26};27
28winston.addColors(colors);29
30const format = winston.format.combine(31  winston.format.timestamp({32    format: ConstantDateFormat.YYYY_MM_DD_HH_MM_SS_MS,33  }),34  winston.format.colorize({all: true}),35  winston.format.printf(36    (info) => `${info.timestamp} ${info.level}: ${info.message}`,37  ),38);39
40const transports = [41  new winston.transports.Console(),42  new winston.transports.File({43    filename: ConstantPath.LOGS_ERROR,44    level: 'error',45  }),46  new winston.transports.File({filename: ConstantPath.LOGS_ALL}),47];48
49const logger = winston.createLogger({50  level: level(),51  levels,52  format,53  transports,54});55
56export default logger;
```

## [Setup MongoDB using Mongoose](#setup-mongodb-using-mongoose)

What is MongoDB?

MongoDB is a NoSQL database used to store structured data. It is a document-oriented database made to operate with documents that resemble JSON.

What is Mongoose?

Mongoose is a MongoDB object modeling library. It is a MongoDB driver for Node.js.

first I’ll install the package:

Terminal window

```
1yarn add mongoose
```

We’ll begin setting up the `mongoose` to connect to the database right away:

`db.config.ts`

```
1import logger from '@/utils/logger.util';2import {connect} from 'mongoose';3
4const connectDb = async (URL: string) => {5  try {6    const connection: any = await connect(URL);7    logger.info(`Mongo DB is connected to: ${connection.connection.host}`);8  } catch (err) {9    logger.error(`An error ocurred\n\r\n\r${err}`);10  }11};12
13export default connectDb;
```

after that, we’ll do some changes to `index.ts`, which is the entry point of the application:

`index.ts`

```
1import connectDb from '@/config/db.config';2// api constant3import ConstantAPI from '@/constants/api.constant';4// http constant5import ConstantHttpCode from '@/constants/http.code.constant';6import ConstantHttpReason from '@/constants/http.reason.constant';7// message constant8import ConstantMessage from '@/constants/message.constant';9// variable10import Variable from '@/env/variable.env';11import Controller from '@/interfaces/controller.interface';12import ErrorMiddleware from '@/middlewares/error.middleware';13import HttpException from '@/utils/exceptions/http.exception';14import compression from 'compression';15import cookieParser from 'cookie-parser';16import cors from 'cors';17import express, {Application, Request, Response, NextFunction} from 'express';18import helmet from 'helmet';19
20class App {21  public app: Application;22  private DATABASE_URL: string;23
24  constructor(controllers: Controller[]) {25    this.app = express();26    this.DATABASE_URL = Variable.DATABASE_URL;27
28    this.initialiseDatabaseConnection(this.DATABASE_URL);29    this.initialiseConfig();30    this.initialiseRoutes();31    this.initialiseControllers(controllers);32    this.initialiseErrorHandling();33  }34
35  private initialiseConfig(): void {36    this.app.use(express.json());37    this.app.use(express.urlencoded({extended: true}));38    this.app.use(cookieParser());39    this.app.use(compression());40    this.app.use(cors());41    this.app.use(helmet());42  }43
44  private initialiseRoutes(): void {45    this.app.get(46      ConstantAPI.ROOT,47      (_req: Request, res: Response, next: NextFunction) => {48        try {49          return res.status(ConstantHttpCode.OK).json({50            status: {51              code: ConstantHttpCode.OK,52              msg: ConstantHttpReason.OK,53            },54            msg: ConstantMessage.API_WORKING,55          });56        } catch (err: any) {57          return next(58            new HttpException(59              ConstantHttpCode.INTERNAL_SERVER_ERROR,60              ConstantHttpReason.INTERNAL_SERVER_ERROR,61              err.message,62            ),63          );64        }65      },66    );67  }68
69  private initialiseControllers(controllers: Controller[]): void {70    controllers.forEach((controller: Controller) => {71      this.app.use(ConstantAPI.API, controller.router);72    });73  }74
75  private initialiseErrorHandling(): void {76    this.app.use(ErrorMiddleware);77  }78
79  private initialiseDatabaseConnection(url: string): void {80    connectDb(url);81  }82}83
84export default App;
```

We will now begin to construct the user schema, but before we do, we must include constants for numbers:

`number.constant.ts`

```
1class Number {2  // user3  public static readonly USERNAME_MIN_LENGTH: number = 3;4  public static readonly USERNAME_MAX_LENGTH: number = 20;5  public static readonly NAME_MIN_LENGTH: number = 3;6  public static readonly NAME_MAX_LENGTH: number = 80;7  public static readonly EMAIL_MAX_LENGTH: number = 50;8  public static readonly PASSWORD_MIN_LENGTH: number = 8;9  public static readonly PHONE_MIN_LENGTH: number = 10;10  public static readonly PHONE_MAX_LENGTH: number = 20;11  public static readonly ADDRESS_MIN_LENGTH: number = 10;12  public static readonly ADDRESS_MAX_LENGTH: number = 200;13}14
15export default Number;
```

`user.schema.ts`

```
1import ConstantNumber from '@/constants/number.constant';2import mongoose from 'mongoose';3
4const UserSchema = new mongoose.Schema(5  {6    username: {7      type: String,8      required: true,9      unique: true,10      min: ConstantNumber.USERNAME_MIN_LENGTH,11      max: ConstantNumber.USERNAME_MAX_LENGTH,12    },13    name: {14      type: String,15      required: true,16      min: ConstantNumber.NAME_MIN_LENGTH,17      max: ConstantNumber.NAME_MAX_LENGTH,18    },19    email: {20      type: String,21      required: true,22      unique: true,23      max: ConstantNumber.EMAIL_MAX_LENGTH,24    },25    password: {26      type: String,27      required: true,28      min: ConstantNumber.PASSWORD_MIN_LENGTH,29    },30    phone: {31      type: String,32      required: true,33      unique: true,34      min: ConstantNumber.PHONE_MIN_LENGTH,35      max: ConstantNumber.PHONE_MAX_LENGTH,36    },37    address: {38      type: String,39      required: true,40      min: ConstantNumber.ADDRESS_MIN_LENGTH,41      max: ConstantNumber.ADDRESS_MAX_LENGTH,42    },43    isAdmin: {44      type: Boolean,45      default: true,46    },47  },48  {49    versionKey: false,50    timestamps: true,51  },52);53
54export default UserSchema;
```

Now, we must create a model to interact with this schema:

`model.constant.ts`

```
1class Model {2  public static readonly USER_MODEL: string = 'UserModel';3}4
5export default Model;
```

`user.interface.ts`

```
1import {Document} from 'mongoose';2
3export default interface User extends Document {4  username: string;5  name: string;6  email: string;7  password: string;8  phone: string;9  address: string;10  isAdmin: boolean;11}
```

`user.model.ts`

```
1import ConstantModel from '@/constants/model.constant';2import UserInterface from '@/interfaces/user.interface';3import UserSchema from '@/schemas/user.schema';4import mongoose from 'mongoose';5
6const UserModel = mongoose.model<UserInterface>(7  ConstantModel.USER_MODEL,8  UserSchema,9);10
11export default UserModel;
```

In my opinion, I build a file to handle all queries in the database through a specific cluster.

`user.repository.ts`

```
1import UserInterface from '@/interfaces/user.interface';2import User from '@/models/user.model';3
4class UserRepository {5  public async findAll(): Promise<UserInterface[]> {6    const users = await User.find({}).select('-password');7    return users;8  }9
10  public async findById(id: string): Promise<UserInterface | null> {11    const user = await User.findById(id).select('-password');12    return user;13  }14
15  public async findByUsername(username: string): Promise<UserInterface | null> {16    const user = await User.findOne({username}).select('-password');17    return user;18  }19
20  public async findByEmail(email: string): Promise<UserInterface | null> {21    const user = await User.findOne({email}).select('-password');22    return user;23  }24
25  public async findByPhone(phone: string): Promise<UserInterface | null> {26    const user = await User.findOne({phone}).select('-password');27    return user;28  }29
30  public async findByIdWithPassword(id: string): Promise<UserInterface | null> {31    const user = await User.findById(id);32    return user;33  }34
35  public async findByUsernameWithPassword(36    username: string,37  ): Promise<UserInterface | null> {38    const user = await User.findOne({username});39    return user;40  }41
42  public async findByEmailWithPassword(43    email: string,44  ): Promise<UserInterface | null> {45    const user = await User.findOne({email});46    return user;47  }48
49  public async findByPhoneWithPassword(50    phone: string,51  ): Promise<UserInterface | null> {52    const user = await User.findOne({phone});53    return user;54  }55
56  public async createUser(user: any): Promise<UserInterface | null> {57    const newUser = new User({58      username: user.username,59      name: user.name,60      email: user.email,61      password: user.password,62      phone: user.phone,63      address: user.address,64      isAdmin: user.isAdmin,65    });66    const savedUser = await newUser.save();67    return savedUser;68  }69
70  public async updateUsername(71    id: string,72    username: string,73  ): Promise<UserInterface | null> {74    const user = await User.findByIdAndUpdate(75      id,76      {username},77      {new: true},78    ).select('-password');79    return user;80  }81
82  public async updateName(83    id: string,84    name: string,85  ): Promise<UserInterface | null> {86    const user = await User.findByIdAndUpdate(id, {name}, {new: true}).select(87      '-password',88    );89    return user;90  }91
92  public async updateEmail(93    id: string,94    email: string,95  ): Promise<UserInterface | null> {96    const user = await User.findByIdAndUpdate(id, {email}, {new: true}).select(97      '-password',98    );99    return user;100  }101
102  public async updatePassword(103    id: string,104    password: string,105  ): Promise<UserInterface | null> {106    const user = await User.findByIdAndUpdate(107      id,108      {password},109      {new: true},110    ).select('-password');111    return user;112  }113
114  public async updatePhone(115    id: string,116    phone: string,117  ): Promise<UserInterface | null> {118    const user = await User.findByIdAndUpdate(id, {phone}, {new: true}).select(119      '-password',120    );121    return user;122  }123
124  public async updateAddress(125    id: string,126    address: string,127  ): Promise<UserInterface | null> {128    const user = await User.findByIdAndUpdate(129      id,130      {address},131      {new: true},132    ).select('-password');133    return user;134  }135
136  public async deleteUser(id: string): Promise<UserInterface | null> {137    const user = await User.findByIdAndDelete(id);138    return user;139  }140
141  public async getUsersStats(lastYear: Date): Promise<UserInterface[] | null> {142    const users = await User.aggregate([143      {$match: {createdAt: {$gte: lastYear}}},144      {145        $project: {146          month: {$month: '$createdAt'},147        },148      },149      {150        $group: {151          _id: '$month',152          total: {$sum: 1},153        },154      },155    ]);156    return users;157  }158}159
160export default UserRepository;
```

## [Setup validation using Joi](#setup-validation-using-joi)

What is Joi?

Joi is a library that helps you validate data. It is a great tool to validate data before you save it to the database.

first I’ll install the package:

Terminal window

```
1yarn add joi
```

`regex.constant.ts`

```
1class Regex {2  public static readonly USERNAME = /^(?!.*\.\.)(?!.*\.$)[^\W][\w.]{3,32}$/;3  public static readonly EMAIL =4    /^(([^<>()\\[\]\\.,;:\s@"]+(\.[^<>()\\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;5  public static readonly PASSWORD =6    /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/;7  public static readonly NAME = /^[a-zA-Z ]{2,35}$/;8  public static readonly PHONE =9    /^\s*(?:\+?(\d{1,3}))?([-. (]*(\d{3})[-. )]*)?((\d{3})[-. ]*(\d{2,4})(?:[-.x ]*(\d+))?)\s*$/;10  public static readonly ADDRESS = /^[a-zA-Z0-9\s,'-]{10,200}$/;11}12
13export default Regex;
```

`user.validation.ts`

```
1import ConstantRegex from '@/constants/regex.constant';2import Joi from 'joi';3
4class UserValidation {5  public register = Joi.object({6    username: Joi.string().max(30).required(),7    name: Joi.string().max(30).required(),8    email: Joi.string().email().required(),9    password: Joi.string().min(6).max(30).required(),10    phone: Joi.string().min(10).max(15).required(),11    address: Joi.string().max(100).required(),12  });13
14  public login = Joi.object({15    email: Joi.string().email().required(),16    password: Joi.string().min(6).max(30).required(),17  });18
19  public updateUsername = Joi.object({20    username: Joi.string().max(30).required(),21    password: Joi.string().min(6).max(30).required(),22  });23
24  public updateName = Joi.object({25    name: Joi.string().max(30).required(),26    password: Joi.string().min(6).max(30).required(),27  });28
29  public updateEmail = Joi.object({30    email: Joi.string().email().required(),31    password: Joi.string().min(6).max(30).required(),32  });33
34  public updatePassword = Joi.object({35    oldPassword: Joi.string().min(6).max(30).required(),36    newPassword: Joi.string().min(6).max(30).required(),37    confirmPassword: Joi.string().min(6).max(30).required(),38  });39
40  public updatePhone = Joi.object({41    phone: Joi.string().min(10).max(15).required(),42    password: Joi.string().min(6).max(30).required(),43  });44
45  public updateAddress = Joi.object({46    address: Joi.string().max(100).required(),47    password: Joi.string().min(6).max(30).required(),48  });49
50  public deleteUser = Joi.object({51    password: Joi.string().min(6).max(30).required(),52  });53
54  public validateUsername(username: string): boolean {55    return ConstantRegex.USERNAME.test(username);56  }57
58  public validateName(name: string): boolean {59    return ConstantRegex.NAME.test(name);60  }61
62  public validateEmail(email: string): boolean {63    return ConstantRegex.EMAIL.test(email);64  }65
66  public validatePassword(password: string): boolean {67    return ConstantRegex.PASSWORD.test(password);68  }69
70  public validatePhone(phone: string): boolean {71    return ConstantRegex.PHONE.test(phone);72  }73
74  public validateAddress(address: string): boolean {75    return ConstantRegex.ADDRESS.test(address);76  }77}78
79export default UserValidation;
```

## [Setup JWT authentication](#setup-jwt-authentication)

What is JWT?

JWT is a JSON Web Token. It is a standard for representing claims to be transferred between parties in a secure way.

An alternative explanation, from a book:

An open industry standard called JSON Web Token is used to exchange data between two entities, often a client (like the front end of your app) and a server (like the back end of your app). They include JSON objects that include the necessary information to be communicated. To ensure that the JSON contents, also known as JWT claims, cannot be changed by the client or an unintentional party, each JWT is additionally signed using cryptography (hashing).

we need to install two libraries:

Terminal window

```
1yarn add jsonwebtoken crypto-js
```

-   `jsonwebtoken` is a library that helps you create, sign, and verify JSON Web Tokens.
-   `crypto-js` is a library that helps you encrypt and decrypt data.

we need to install the types for the library:

Terminal window

```
1yarn add @types/jsonwebtoken @types/crypto-js
```

to encrypt, decode, and produce an access token, create a file:

`user.security.ts`

```
1import Variable from '@/env/variable.env';2import CryptoJS from 'crypto-js';3import jwt from 'jsonwebtoken';4
5class UserSecurity {6  public encrypt(password: string): string {7    return CryptoJS.AES.encrypt(password, Variable.PASS_SECRET).toString();8  }9
10  public decrypt(password: string): string {11    return CryptoJS.AES.decrypt(password, Variable.PASS_SECRET).toString(12      CryptoJS.enc.Utf8,13    );14  }15
16  public comparePassword(password: string, decryptedPassword: string): boolean {17    return password === this.decrypt(decryptedPassword);18  }19
20  public generateAccessToken(id: string, isAdmin: boolean): string {21    const token = jwt.sign({id, isAdmin}, Variable.JWT_SECRET, {22      expiresIn: '3d',23    });24
25    return `Bearer ${token}`;26  }27}28
29export default UserSecurity;
```

`message.constant.ts`

```
1class Message {2  ...3
4  // auth5  public static readonly USERNAME_NOT_VALID: string = 'username is not valid'6  public static readonly NAME_NOT_VALID: string = 'name is not valid'7  public static readonly EMAIL_NOT_VALID: string = 'email is not valid'8  public static readonly PASSWORD_NOT_VALID: string = 'password is not valid'9  public static readonly PHONE_NOT_VALID: string = 'phone is not valid'10  public static readonly ADDRESS_NOT_VALID: string = 'address is not valid'11  public static readonly USERNAME_EXIST: string = 'username is exist'12  public static readonly EMAIL_EXIST: string = 'email is exist'13  public static readonly PHONE_EXIST: string = 'phone is exist'14  public static readonly USER_NOT_CREATE: string =15    'user is not create, please try again'16  public static readonly USER_CREATE_SUCCESS: string =17    'user is create success, please login'18  public static readonly USER_NOT_FOUND: string = 'user is not found'19  public static readonly PASSWORD_NOT_MATCH: string = 'password is not match'20  public static readonly USER_LOGIN_SUCCESS: string = 'user is login success'21}22export default Message
```

using the user validation form joi as input, construct middleware:

`validation.middleware.ts`

```
1// http constant2import ConstantHttpCode from '@/constants/http.code.constant';3import ConstantHttpReason from '@/constants/http.reason.constant';4import {Request, Response, NextFunction, RequestHandler} from 'express';5import Joi from 'joi';6
7const validationMiddleware = (schema: Joi.Schema): RequestHandler => {8  return async (9    req: Request,10    res: Response,11    next: NextFunction,12  ): Promise<void> => {13    const validationOptions = {14      abortEarly: false,15      allowUnknown: true,16      stripUnknown: true,17    };18
19    try {20      const value = await schema.validateAsync(req.body, validationOptions);21      req.body = value;22      next();23    } catch (e: any) {24      const errors: string[] = [];25      e.details.forEach((error: Joi.ValidationErrorItem) => {26        errors.push(error.message);27      });28
29      res.status(ConstantHttpCode.NOT_FOUND).send({30        status: {31          code: ConstantHttpCode.NOT_FOUND,32          msg: ConstantHttpReason.NOT_FOUND,33        },34        msg: errors,35      });36    }37  };38};39
40export default validationMiddleware;
```

We’ll now link the repository, security, and a service for authentication:

`auth.service.ts`

```
1import UserRepository from '@/repositories/user.repository';2import UserSecurity from '@/security/user.security';3
4class AuthService {5  private userRepository: UserRepository;6  private userSecurity: UserSecurity;7
8  constructor() {9    this.userRepository = new UserRepository();10    this.userSecurity = new UserSecurity();11  }12
13  public async findByUsername(username: string): Promise<any> {14    const user = await this.userRepository.findByUsername(username);15    return user;16  }17
18  public async findByEmail(email: string): Promise<any> {19    const user = await this.userRepository.findByEmail(email);20    return user;21  }22
23  public async findByPhone(phone: string): Promise<any> {24    const user = await this.userRepository.findByPhone(phone);25    return user;26  }27
28  public async findByEmailWithPassword(email: string): Promise<any> {29    const user = await this.userRepository.findByEmailWithPassword(email);30    return user;31  }32
33  public comparePassword(password: string, decryptedPassword: string): boolean {34    return this.userSecurity.comparePassword(password, decryptedPassword);35  }36
37  public async createUser(user: any): Promise<any> {38    const encryptedPassword = this.userSecurity.encrypt(user.password);39    const newUser = {40      username: user.username,41      name: user.name,42      email: user.email,43      password: encryptedPassword,44      phone: user.phone,45      address: user.address,46      isAdmin: user.isAdmin,47    };48    const savedUser = await this.userRepository.createUser(newUser);49    return savedUser;50  }51
52  public async generateAccessToken(53    id: string,54    isAdmin: boolean,55  ): Promise<string> {56    const token = this.userSecurity.generateAccessToken(id, isAdmin);57    return token;58  }59}60
61export default AuthService;
```

`api.constant.ts`

```
1class Api {2  ...3
4  // auth5  public static readonly AUTH_REGISTER: string = '/register'6  public static readonly AUTH_LOGIN: string = '/login'7}8export default Api
```

`auth.controller.ts`

```
1// api constant2import ConstantAPI from '@/constants/api.constant';3// http constant4import ConstantHttpCode from '@/constants/http.code.constant';5import ConstantHttpReason from '@/constants/http.reason.constant';6// message constant7import ConstantMessage from '@/constants/message.constant';8import Controller from '@/interfaces/controller.interface';9import validationMiddleware from '@/middlewares/validation.middleware';10import AuthService from '@/services/auth.service';11import HttpException from '@/utils/exceptions/http.exception';12// logger13import logger from '@/utils/logger.util';14import Validate from '@/validations/user.validation';15import {Router, Request, Response, NextFunction} from 'express';16
17class AuthController implements Controller {18  public path: string;19  public router: Router;20  private authService: AuthService;21  private validate: Validate;22
23  constructor() {24    this.path = ConstantAPI.AUTH;25    this.router = Router();26    this.authService = new AuthService();27    this.validate = new Validate();28
29    this.initialiseRoutes();30  }31
32  private initialiseRoutes(): void {33    this.router.post(34      `${this.path}${ConstantAPI.AUTH_REGISTER}`,35      validationMiddleware(this.validate.register),36      this.register,37    );38
39    this.router.post(40      `${this.path}${ConstantAPI.AUTH_LOGIN}`,41      validationMiddleware(this.validate.login),42      this.login,43    );44  }45
46  private register = async (47    req: Request,48    res: Response,49    next: NextFunction,50  ): Promise<Response | void> => {51    try {52      const {username, name, email, password, phone, address} = req.body;53
54      const usernameValidated = this.validate.validateUsername(username);55      if (!usernameValidated) {56        return next(57          new HttpException(58            ConstantHttpCode.CONFLICT,59            ConstantHttpReason.CONFLICT,60            ConstantMessage.USERNAME_NOT_VALID,61          ),62        );63      }64      logger.info(`username ${username} is valid`);65
66      const nameValidated = this.validate.validateName(name);67      if (!nameValidated) {68        return next(69          new HttpException(70            ConstantHttpCode.CONFLICT,71            ConstantHttpReason.CONFLICT,72            ConstantMessage.NAME_NOT_VALID,73          ),74        );75      }76      logger.info(`name ${name} is valid`);77
78      const emailValidated = this.validate.validateEmail(email);79      if (!emailValidated) {80        return next(81          new HttpException(82            ConstantHttpCode.CONFLICT,83            ConstantHttpReason.CONFLICT,84            ConstantMessage.EMAIL_NOT_VALID,85          ),86        );87      }88      logger.info(`email ${email} is valid`);89
90      const passwordValidated = this.validate.validatePassword(password);91      if (!passwordValidated) {92        return next(93          new HttpException(94            ConstantHttpCode.CONFLICT,95            ConstantHttpReason.CONFLICT,96            ConstantMessage.PASSWORD_NOT_VALID,97          ),98        );99      }100      logger.info(`password ${password} is valid`);101
102      const phoneValidated = this.validate.validatePhone(phone);103      if (!phoneValidated) {104        return next(105          new HttpException(106            ConstantHttpCode.CONFLICT,107            ConstantHttpReason.CONFLICT,108            ConstantMessage.PHONE_NOT_VALID,109          ),110        );111      }112      logger.info(`phone ${phone} is valid`);113
114      const addressValidated = this.validate.validateAddress(address);115      if (!addressValidated) {116        return next(117          new HttpException(118            ConstantHttpCode.CONFLICT,119            ConstantHttpReason.CONFLICT,120            ConstantMessage.ADDRESS_NOT_VALID,121          ),122        );123      }124      logger.info(`address ${address} is valid`);125
126      const usernameCheck = await this.authService.findByUsername(username);127      if (usernameCheck) {128        return next(129          new HttpException(130            ConstantHttpCode.CONFLICT,131            ConstantHttpReason.CONFLICT,132            ConstantMessage.USERNAME_EXIST,133          ),134        );135      }136
137      const emailCheck = await this.authService.findByEmail(email);138      if (emailCheck) {139        return next(140          new HttpException(141            ConstantHttpCode.CONFLICT,142            ConstantHttpReason.CONFLICT,143            ConstantMessage.EMAIL_EXIST,144          ),145        );146      }147
148      const phoneCheck = await this.authService.findByPhone(phone);149      if (phoneCheck) {150        return next(151          new HttpException(152            ConstantHttpCode.CONFLICT,153            ConstantHttpReason.CONFLICT,154            ConstantMessage.PHONE_EXIST,155          ),156        );157      }158
159      const newUserData = {160        username,161        name,162        email,163        password,164        phone,165        address,166      };167
168      const user = await this.authService.createUser(newUserData);169      if (!user) {170        return next(171          new HttpException(172            ConstantHttpCode.CONFLICT,173            ConstantHttpReason.CONFLICT,174            ConstantMessage.USER_NOT_CREATE,175          ),176        );177      }178
179      const newUser = {...user}._doc;180
181      logger.info({newUserpassword: newUser.password});182
183      delete newUser.password;184
185      logger.info({newUserpassword: newUser.password});186
187      return res.status(ConstantHttpCode.CREATED).json({188        status: {189          code: ConstantHttpCode.CREATED,190          msg: ConstantHttpReason.CREATED,191        },192        msg: ConstantMessage.USER_CREATE_SUCCESS,193        data: newUser,194      });195    } catch (err: any) {196      return next(197        new HttpException(198          ConstantHttpCode.INTERNAL_SERVER_ERROR,199          ConstantHttpReason.INTERNAL_SERVER_ERROR,200          err.message,201        ),202      );203    }204  };205
206  private login = async (207    req: Request,208    res: Response,209    next: NextFunction,210  ): Promise<Response | void> => {211    try {212      const {email, password} = req.body;213
214      const emailValidated = this.validate.validateEmail(email);215      if (!emailValidated) {216        return next(217          new HttpException(218            ConstantHttpCode.INTERNAL_SERVER_ERROR,219            ConstantHttpReason.INTERNAL_SERVER_ERROR,220            ConstantMessage.EMAIL_NOT_VALID,221          ),222        );223      }224      logger.info(`email ${email} is valid`);225
226      const passwordValidated = this.validate.validatePassword(password);227      if (!passwordValidated) {228        return next(229          new HttpException(230            ConstantHttpCode.INTERNAL_SERVER_ERROR,231            ConstantHttpReason.INTERNAL_SERVER_ERROR,232            ConstantMessage.PASSWORD_NOT_VALID,233          ),234        );235      }236      logger.info(`password ${password} is valid`);237
238      const user = await this.authService.findByEmailWithPassword(email);239      if (!user) {240        return next(241          new HttpException(242            ConstantHttpCode.INTERNAL_SERVER_ERROR,243            ConstantHttpReason.INTERNAL_SERVER_ERROR,244            ConstantMessage.USER_NOT_FOUND,245          ),246        );247      }248
249      const isMatch = this.authService.comparePassword(password, user.password);250      if (!isMatch) {251        return next(252          new HttpException(253            ConstantHttpCode.INTERNAL_SERVER_ERROR,254            ConstantHttpReason.INTERNAL_SERVER_ERROR,255            ConstantMessage.PASSWORD_NOT_MATCH,256          ),257        );258      }259
260      const accessToken = await this.authService.generateAccessToken(261        user.id,262        user.isAdmin,263      );264      logger.info(`accessToken: ${accessToken}`);265
266      const newUser = {...user}._doc;267
268      logger.info({newUserpassword: newUser.password});269
270      delete newUser.password;271
272      logger.info({newUserpassword: newUser.password});273
274      return res.status(ConstantHttpCode.OK).json({275        status: {276          code: ConstantHttpCode.OK,277          msg: ConstantHttpReason.OK,278        },279        msg: ConstantMessage.USER_LOGIN_SUCCESS,280        data: {281          user: newUser,282          accessToken,283        },284      });285    } catch (err: any) {286      return next(287        new HttpException(288          ConstantHttpCode.INTERNAL_SERVER_ERROR,289          ConstantHttpReason.INTERNAL_SERVER_ERROR,290          err.message,291        ),292      );293    }294  };295}296
297export default AuthController;
```

We will now develop a validation for each JWT that we receive:

`token.validation.ts`

```
1// http constant2import ConstantHttpCode from '@/constants/http.code.constant';3import ConstantHttpReason from '@/constants/http.reason.constant';4// message constant5import ConstantMessage from '@/constants/message.constant';6// variable7import Variable from '@/env/variable.env';8import HttpException from '@/utils/exceptions/http.exception';9import {verifyToken} from '@/validations/token.validation';10import {Request, Response, NextFunction} from 'express';11import jwt from 'jsonwebtoken';12
13export const verifyToken = async (14  req: Request,15  res: Response,16  next: NextFunction,17) => {18  const bearer = req.headers.authorization;19  logger.info(`bearer: ${bearer}`);20
21  if (!bearer) {22    return next(23      new HttpException(24        ConstantHttpCode.UNAUTHORIZED,25        ConstantHttpReason.UNAUTHORIZED,26        ConstantMessage.TOKEN_NOT_VALID,27      ),28    );29  }30
31  if (!bearer || !bearer.startsWith('Bearer ')) {32    return next(33      new HttpException(34        ConstantHttpCode.UNAUTHORIZED,35        ConstantHttpReason.UNAUTHORIZED,36        ConstantMessage.UNAUTHORIZED,37      ),38    );39  }40
41  const accessToken = bearer.split('Bearer ')[1].trim();42
43  return jwt.verify(accessToken, Variable.JWT_SECRET, (err, user: any) => {44    if (err) {45      res.status(ConstantHttpCode.FORBIDDEN).json({46        status: {47          code: ConstantHttpCode.FORBIDDEN,48          msg: ConstantHttpReason.FORBIDDEN,49        },50        msg: ConstantMessage.TOKEN_NOT_VALID,51      });52    }53    req.user = user;54    return next();55  });56};57
58export default {verifyToken};
```

Before moving on, we must make a few adjustments to the configuration file and type in the typescript request:

`index.d.ts`

```
1import User from '@/interfaces/user.interface';2
3declare global {4  namespace Express {5    export interface Request {6      user: User;7    }8  }9}
```

`tsconfig.json`

```
1{2  ...,3  "typeRoots": [4    "./src/types",5    "./node_modules/@types"6  ],7  ...8}
```

`message.constant.ts`

```
1class Message {2  ...3
4  // token5  public static readonly TOKEN_NOT_VALID: string = 'Token not valid'6  public static readonly NOT_AUTHENTICATED: string = 'Not authenticated'7  public static readonly UNAUTHORIZED: string = 'Unauthorized'8  public static readonly NOT_ALLOWED: string = 'Not allowed'9}10export default Message
```

After that, we can develop middleware to check if the request has authorization for the same end point:

`authenticated.middleware.ts`

```
1// http constant2import ConstantHttpCode from '@/constants/http.code.constant';3import ConstantHttpReason from '@/constants/http.reason.constant';4// message constant5import ConstantMessage from '@/constants/message.constant';6import HttpException from '@/utils/exceptions/http.exception';7import {verifyToken} from '@/validations/token.validation';8import {Request, Response, NextFunction} from 'express';9
10class AuthenticatedMiddleware {11  public async verifyTokenAndAuthorization(12    req: Request,13    res: Response,14    next: NextFunction,15  ) {16    verifyToken(req, res, () => {17      if (req?.user?.id === req?.params?.id || req?.user?.isAdmin) {18        return next();19      }20
21      return next(22        new HttpException(23          ConstantHttpCode.FORBIDDEN,24          ConstantHttpReason.FORBIDDEN,25          ConstantMessage.NOT_ALLOWED,26        ),27      );28    });29  }30
31  public async verifyTokenAndAdmin(32    req: Request,33    res: Response,34    next: NextFunction,35  ) {36    verifyToken(req, res, () => {37      if (req?.user?.isAdmin) {38        return next();39      }40
41      return next(42        new HttpException(43          ConstantHttpCode.FORBIDDEN,44          ConstantHttpReason.FORBIDDEN,45          ConstantMessage.NOT_ALLOWED,46        ),47      );48    });49  }50}51
52export default AuthenticatedMiddleware;
```

We’ll now link the repository, security, and service for user:

`user.service.ts`

```
1import UserRepository from '@/repositories/user.repository';2import UserSecurity from '@/security/user.security';3
4class UserService {5  private userRepository: UserRepository;6  private userSecurity: UserSecurity;7
8  constructor() {9    this.userRepository = new UserRepository();10    this.userSecurity = new UserSecurity();11  }12
13  public comparePassword(password: string, encryptedPassword: string): boolean {14    return this.userSecurity.comparePassword(password, encryptedPassword);15  }16
17  public async findAll(): Promise<any> {18    const users = await this.userRepository.findAll();19    return users;20  }21
22  public async findById(id: string): Promise<any> {23    const user = await this.userRepository.findById(id);24    return user;25  }26
27  public async findByUsername(username: string): Promise<any> {28    const user = await this.userRepository.findByUsername(username);29    return user;30  }31
32  public async findByEmail(email: string): Promise<any> {33    const user = await this.userRepository.findByEmail(email);34    return user;35  }36
37  public async findByPhone(phone: string): Promise<any> {38    const user = await this.userRepository.findByPhone(phone);39    return user;40  }41
42  public async findByIdWithPassword(id: string): Promise<any> {43    const user = await this.userRepository.findByIdWithPassword(id);44    return user;45  }46
47  public async updateUsername(id: string, username: string): Promise<any> {48    const user = await this.userRepository.updateUsername(id, username);49    return user;50  }51
52  public async updateName(id: string, name: string): Promise<any> {53    const user = await this.userRepository.updateName(id, name);54    return user;55  }56
57  public async updateEmail(id: string, email: string): Promise<any> {58    const user = await this.userRepository.updateEmail(id, email);59    return user;60  }61
62  public async updatePassword(id: string, password: string): Promise<any> {63    const encryptedPassword = this.userSecurity.encrypt(password);64    const user = await this.userRepository.updatePassword(65      id,66      encryptedPassword,67    );68    return user;69  }70
71  public async updatePhone(id: string, phone: string): Promise<any> {72    const user = await this.userRepository.updatePhone(id, phone);73    return user;74  }75
76  public async updateAddress(id: string, address: string): Promise<any> {77    const user = await this.userRepository.updateAddress(id, address);78    return user;79  }80
81  public async deleteUser(id: string): Promise<any> {82    const user = await this.userRepository.deleteUser(id);83    return user;84  }85
86  public async getUsersStats(): Promise<any> {87    const date = new Date();88    const lastYear = new Date(date.setFullYear(date.getFullYear() - 1));89    const usersStats = await this.userRepository.getUsersStats(lastYear);90    return usersStats;91  }92}93
94export default UserService;
```

`api.constant.ts`

```
1class Api {2  ...3
4  // users5  public static readonly USER_UPDATE_USERNAME: string = '/update-username/:id'6  public static readonly USER_UPDATE_NAME: string = '/update-name/:id'7  public static readonly USER_UPDATE_EMAIL: string = '/update-email/:id'8  public static readonly USER_UPDATE_PASSWORD: string = '/update-password/:id'9  public static readonly USER_UPDATE_PHONE: string = '/update-phone/:id'10  public static readonly USER_UPDATE_ADDRESS: string = '/update-address/:id'11  public static readonly USER_DELETE: string = '/delete/:id'12  public static readonly USER_GET: string = '/find/:id'13  public static readonly USER_GET_ALL: string = '/'14  public static readonly USER_GET_ALL_STATS: string = '/stats'15}16export default Api
```

`message.constant.ts`

```
1class Message {2  ...3
4  // user5  public static readonly USERNAME_NOT_CHANGE: string = 'username is not change'6  public static readonly USERNAME_CHANGE_SUCCESS: string =7    'username is change success'8  public static readonly NAME_NOT_CHANGE: string = 'name is not change'9  public static readonly NAME_CHANGE_SUCCESS: string = 'name is change success'10  public static readonly EMAIL_NOT_CHANGE: string = 'email is not change'11  public static readonly EMAIL_CHANGE_SUCCESS: string =12    'email is change success'13  public static readonly PASSWORD_NOT_CHANGE: string = 'password is not change'14  public static readonly PASSWORD_CHANGE_SUCCESS: string =15    'password is change success'16  public static readonly PHONE_NOT_CHANGE: string = 'phone is not change'17  public static readonly PHONE_CHANGE_SUCCESS: string =18    'phone is change success'19  public static readonly ADDRESS_NOT_CHANGE: string = 'address is not change'20  public static readonly ADDRESS_CHANGE_SUCCESS: string =21    'address is change success'22  public static readonly USER_NOT_DELETE: string =23    'user is not delete, please try again'24  public static readonly USER_DELETE_SUCCESS: string = 'user is delete success'25  public static readonly USER_FOUND: string = 'user is found'26}27export default Message
```

To access the service for what we need to build the methods, we’ll make a new contact with the middleware for each authorized user and input validation:

`user.controller.ts`

```
1// api constant2import ConstantAPI from '@/constants/api.constant';3// http constant4import ConstantHttpCode from '@/constants/http.code.constant';5import ConstantHttpReason from '@/constants/http.reason.constant';6// message constant7import ConstantMessage from '@/constants/message.constant';8import Controller from '@/interfaces/controller.interface';9import Authenticated from '@/middlewares/authenticated.middleware';10import validationMiddleware from '@/middlewares/validation.middleware';11import UserService from '@/services/user.service';12import HttpException from '@/utils/exceptions/http.exception';13// logger14import logger from '@/utils/logger.util';15import Validate from '@/validations/user.validation';16import {Router, Request, Response, NextFunction} from 'express';17
18class UserController implements Controller {19  public path: string;20  public router: Router;21  private userService: UserService;22  private authenticated: Authenticated;23  private validate: Validate;24
25  constructor() {26    this.path = ConstantAPI.USERS;27    this.router = Router();28    this.userService = new UserService();29    this.authenticated = new Authenticated();30    this.validate = new Validate();31
32    this.initialiseRoutes();33  }34
35  private initialiseRoutes(): void {36    this.router.post(37      `${this.path}${ConstantAPI.USER_UPDATE_USERNAME}`,38      this.authenticated.verifyTokenAndAuthorization,39      validationMiddleware(this.validate.updateUsername),40      this.updateUsername,41    );42
43    this.router.post(44      `${this.path}${ConstantAPI.USER_UPDATE_NAME}`,45      this.authenticated.verifyTokenAndAuthorization,46      validationMiddleware(this.validate.updateName),47      this.updateName,48    );49
50    this.router.post(51      `${this.path}${ConstantAPI.USER_UPDATE_EMAIL}`,52      this.authenticated.verifyTokenAndAuthorization,53      validationMiddleware(this.validate.updateEmail),54      this.updateEmail,55    );56
57    this.router.post(58      `${this.path}${ConstantAPI.USER_UPDATE_PASSWORD}`,59      this.authenticated.verifyTokenAndAuthorization,60      validationMiddleware(this.validate.updatePassword),61      this.updatePassword,62    );63
64    this.router.post(65      `${this.path}${ConstantAPI.USER_UPDATE_PHONE}`,66      this.authenticated.verifyTokenAndAuthorization,67      validationMiddleware(this.validate.updatePhone),68      this.updatePhone,69    );70
71    this.router.post(72      `${this.path}${ConstantAPI.USER_UPDATE_ADDRESS}`,73      this.authenticated.verifyTokenAndAuthorization,74      validationMiddleware(this.validate.updateAddress),75      this.updateAddress,76    );77
78    this.router.post(79      `${this.path}${ConstantAPI.USER_DELETE}`,80      this.authenticated.verifyTokenAndAuthorization,81      validationMiddleware(this.validate.deleteUser),82      this.deleteUser,83    );84
85    this.router.get(86      `${this.path}${ConstantAPI.USER_GET}`,87      this.authenticated.verifyTokenAndAuthorization,88      this.getUser,89    );90
91    this.router.get(92      `${this.path}${ConstantAPI.USER_GET_ALL}`,93      this.authenticated.verifyTokenAndAdmin,94      this.getAllUsers,95    );96
97    this.router.get(98      `${this.path}${ConstantAPI.USER_GET_ALL_STATS}`,99      this.authenticated.verifyTokenAndAdmin,100      this.getUsersStats,101    );102  }103
104  private updateUsername = async (105    req: Request,106    res: Response,107    next: NextFunction,108  ): Promise<Response | void> => {109    try {110      const {username, password} = req.body;111      const {id} = req.params;112
113      const user = await this.userService.findByIdWithPassword(id);114      if (!user) {115        return next(116          new HttpException(117            ConstantHttpCode.NOT_FOUND,118            ConstantHttpReason.NOT_FOUND,119            ConstantMessage.USER_NOT_FOUND,120          ),121        );122      }123      logger.info(`user ${user.username} found`);124
125      const isUsernameValid = this.validate.validateUsername(username);126      if (!isUsernameValid) {127        return next(128          new HttpException(129            ConstantHttpCode.BAD_REQUEST,130            ConstantHttpReason.BAD_REQUEST,131            ConstantMessage.USERNAME_NOT_VALID,132          ),133        );134      }135      logger.info(`username ${username} is valid`);136
137      const isPasswordValid = this.validate.validatePassword(password);138      if (!isPasswordValid) {139        return next(140          new HttpException(141            ConstantHttpCode.BAD_REQUEST,142            ConstantHttpReason.BAD_REQUEST,143            ConstantMessage.PASSWORD_NOT_VALID,144          ),145        );146      }147      logger.info(`password ${password} is valid`);148
149      const isMatch = this.userService.comparePassword(password, user.password);150      if (!isMatch) {151        return next(152          new HttpException(153            ConstantHttpCode.UNAUTHORIZED,154            ConstantHttpReason.UNAUTHORIZED,155            ConstantMessage.PASSWORD_NOT_MATCH,156          ),157        );158      }159      logger.info(`password ${password} match`);160
161      const usernameCheck = await this.userService.findByUsername(username);162      if (usernameCheck) {163        return next(164          new HttpException(165            ConstantHttpCode.BAD_REQUEST,166            ConstantHttpReason.BAD_REQUEST,167            ConstantMessage.USERNAME_EXIST,168          ),169        );170      }171
172      if (user.username === username) {173        return next(174          new HttpException(175            ConstantHttpCode.BAD_REQUEST,176            ConstantHttpReason.BAD_REQUEST,177            ConstantMessage.USERNAME_NOT_CHANGE,178          ),179        );180      }181
182      const updatedUser = await this.userService.updateUsername(id, username);183      if (!updatedUser) {184        return next(185          new HttpException(186            ConstantHttpCode.BAD_REQUEST,187            ConstantHttpReason.BAD_REQUEST,188            ConstantMessage.USERNAME_NOT_CHANGE,189          ),190        );191      }192      logger.info(`user ${user.username} updated`);193
194      return res.status(ConstantHttpCode.OK).json({195        status: {196          code: ConstantHttpCode.OK,197          msg: ConstantHttpReason.OK,198        },199        msg: ConstantMessage.USERNAME_CHANGE_SUCCESS,200        data: {201          user: updatedUser,202        },203      });204    } catch (err: any) {205      next(206        new HttpException(207          ConstantHttpCode.INTERNAL_SERVER_ERROR,208          ConstantHttpReason.INTERNAL_SERVER_ERROR,209          err?.message,210        ),211      );212    }213  };214
215  private updateName = async (216    req: Request,217    res: Response,218    next: NextFunction,219  ): Promise<Response | void> => {220    try {221      const {name, password} = req.body;222      const {id} = req.params;223
224      const user = await this.userService.findByIdWithPassword(id);225      if (!user) {226        return next(227          new HttpException(228            ConstantHttpCode.NOT_FOUND,229            ConstantHttpReason.NOT_FOUND,230            ConstantMessage.USER_NOT_FOUND,231          ),232        );233      }234      logger.info(`user ${user.username} found`);235
236      const isNameValid = this.validate.validateName(name);237      if (!isNameValid) {238        return next(239          new HttpException(240            ConstantHttpCode.BAD_REQUEST,241            ConstantHttpReason.BAD_REQUEST,242            ConstantMessage.NAME_NOT_VALID,243          ),244        );245      }246      logger.info(`name ${name} is valid`);247
248      const isPasswordValid = this.validate.validatePassword(password);249      if (!isPasswordValid) {250        return next(251          new HttpException(252            ConstantHttpCode.BAD_REQUEST,253            ConstantHttpReason.BAD_REQUEST,254            ConstantMessage.PASSWORD_NOT_VALID,255          ),256        );257      }258      logger.info(`password ${password} is valid`);259
260      const isMatch = this.userService.comparePassword(password, user.password);261      if (!isMatch) {262        return next(263          new HttpException(264            ConstantHttpCode.UNAUTHORIZED,265            ConstantHttpReason.UNAUTHORIZED,266            ConstantMessage.PASSWORD_NOT_MATCH,267          ),268        );269      }270      logger.info(`password ${password} match`);271
272      if (user.name === name) {273        return next(274          new HttpException(275            ConstantHttpCode.BAD_REQUEST,276            ConstantHttpReason.BAD_REQUEST,277            ConstantMessage.NAME_NOT_CHANGE,278          ),279        );280      }281
282      const updatedUser = await this.userService.updateName(id, name);283      if (!updatedUser) {284        return next(285          new HttpException(286            ConstantHttpCode.BAD_REQUEST,287            ConstantHttpReason.BAD_REQUEST,288            ConstantMessage.NAME_NOT_CHANGE,289          ),290        );291      }292      logger.info(`user ${user.username} updated`);293
294      return res.status(ConstantHttpCode.OK).json({295        status: {296          code: ConstantHttpCode.OK,297          msg: ConstantHttpReason.OK,298        },299        msg: ConstantMessage.NAME_CHANGE_SUCCESS,300        data: {301          user: updatedUser,302        },303      });304    } catch (err: any) {305      next(306        new HttpException(307          ConstantHttpCode.INTERNAL_SERVER_ERROR,308          ConstantHttpReason.INTERNAL_SERVER_ERROR,309          err?.message,310        ),311      );312    }313  };314
315  private updateEmail = async (316    req: Request,317    res: Response,318    next: NextFunction,319  ): Promise<Response | void> => {320    try {321      const {email, password} = req.body;322      const {id} = req.params;323
324      const user = await this.userService.findByIdWithPassword(id);325      if (!user) {326        return next(327          new HttpException(328            ConstantHttpCode.NOT_FOUND,329            ConstantHttpReason.NOT_FOUND,330            ConstantMessage.USER_NOT_FOUND,331          ),332        );333      }334
335      const isEmailValid = this.validate.validateEmail(email);336      if (!isEmailValid) {337        return next(338          new HttpException(339            ConstantHttpCode.BAD_REQUEST,340            ConstantHttpReason.BAD_REQUEST,341            ConstantMessage.EMAIL_NOT_VALID,342          ),343        );344      }345
346      const isPasswordValid = this.validate.validatePassword(password);347      if (!isPasswordValid) {348        return next(349          new HttpException(350            ConstantHttpCode.BAD_REQUEST,351            ConstantHttpReason.BAD_REQUEST,352            ConstantMessage.PASSWORD_NOT_VALID,353          ),354        );355      }356
357      if (user.email === email) {358        return next(359          new HttpException(360            ConstantHttpCode.BAD_REQUEST,361            ConstantHttpReason.BAD_REQUEST,362            ConstantMessage.EMAIL_NOT_CHANGE,363          ),364        );365      }366
367      const emailCheck = await this.userService.findByEmail(email);368      if (emailCheck) {369        return next(370          new HttpException(371            ConstantHttpCode.BAD_REQUEST,372            ConstantHttpReason.BAD_REQUEST,373            ConstantMessage.EMAIL_EXIST,374          ),375        );376      }377
378      const isMatch = this.userService.comparePassword(password, user.password);379      if (!isMatch) {380        return next(381          new HttpException(382            ConstantHttpCode.UNAUTHORIZED,383            ConstantHttpReason.UNAUTHORIZED,384            ConstantMessage.PASSWORD_NOT_MATCH,385          ),386        );387      }388
389      const updatedUser = await this.userService.updateEmail(id, email);390      if (!updatedUser) {391        return next(392          new HttpException(393            ConstantHttpCode.BAD_REQUEST,394            ConstantHttpReason.BAD_REQUEST,395            ConstantMessage.EMAIL_NOT_CHANGE,396          ),397        );398      }399
400      return res.status(ConstantHttpCode.OK).json({401        status: {402          code: ConstantHttpCode.OK,403          msg: ConstantHttpReason.OK,404        },405        msg: ConstantMessage.EMAIL_CHANGE_SUCCESS,406        data: {407          user: updatedUser,408        },409      });410    } catch (err: any) {411      next(412        new HttpException(413          ConstantHttpCode.INTERNAL_SERVER_ERROR,414          ConstantHttpReason.INTERNAL_SERVER_ERROR,415          err?.message,416        ),417      );418    }419  };420
421  private updatePassword = async (422    req: Request,423    res: Response,424    next: NextFunction,425  ): Promise<Response | void> => {426    try {427      const {oldPassword, newPassword, confirmPassword} = req.body;428      const {id} = req.params;429
430      if (newPassword !== confirmPassword) {431        return next(432          new HttpException(433            ConstantHttpCode.BAD_REQUEST,434            ConstantHttpReason.BAD_REQUEST,435            ConstantMessage.PASSWORD_NOT_MATCH,436          ),437        );438      }439
440      const user = await this.userService.findByIdWithPassword(id);441      if (!user) {442        return next(443          new HttpException(444            ConstantHttpCode.NOT_FOUND,445            ConstantHttpReason.NOT_FOUND,446            ConstantMessage.USER_NOT_FOUND,447          ),448        );449      }450
451      const isOldPasswordValid = this.validate.validatePassword(oldPassword);452      if (!isOldPasswordValid) {453        return next(454          new HttpException(455            ConstantHttpCode.BAD_REQUEST,456            ConstantHttpReason.BAD_REQUEST,457            ConstantMessage.PASSWORD_NOT_VALID,458          ),459        );460      }461
462      const isNewPasswordValid = this.validate.validatePassword(newPassword);463      if (!isNewPasswordValid) {464        return next(465          new HttpException(466            ConstantHttpCode.BAD_REQUEST,467            ConstantHttpReason.BAD_REQUEST,468            ConstantMessage.PASSWORD_NOT_VALID,469          ),470        );471      }472
473      const isConfirmPasswordValid =474        this.validate.validatePassword(confirmPassword);475      if (!isConfirmPasswordValid) {476        return next(477          new HttpException(478            ConstantHttpCode.BAD_REQUEST,479            ConstantHttpReason.BAD_REQUEST,480            ConstantMessage.PASSWORD_NOT_VALID,481          ),482        );483      }484
485      const isMatch = this.userService.comparePassword(486        oldPassword,487        user.password,488      );489      if (!isMatch) {490        return next(491          new HttpException(492            ConstantHttpCode.UNAUTHORIZED,493            ConstantHttpReason.UNAUTHORIZED,494            ConstantMessage.PASSWORD_NOT_MATCH,495          ),496        );497      }498
499      if (oldPassword === newPassword) {500        return next(501          new HttpException(502            ConstantHttpCode.BAD_REQUEST,503            ConstantHttpReason.BAD_REQUEST,504            ConstantMessage.PASSWORD_NOT_CHANGE,505          ),506        );507      }508
509      const updatedUser = await this.userService.updatePassword(510        id,511        newPassword,512      );513      if (!updatedUser) {514        return next(515          new HttpException(516            ConstantHttpCode.BAD_REQUEST,517            ConstantHttpReason.BAD_REQUEST,518            ConstantMessage.PASSWORD_NOT_CHANGE,519          ),520        );521      }522
523      return res.status(ConstantHttpCode.OK).json({524        status: {525          code: ConstantHttpCode.OK,526          msg: ConstantHttpReason.OK,527        },528        msg: ConstantMessage.PASSWORD_CHANGE_SUCCESS,529        data: {530          user: updatedUser,531        },532      });533    } catch (err: any) {534      next(535        new HttpException(536          ConstantHttpCode.INTERNAL_SERVER_ERROR,537          ConstantHttpReason.INTERNAL_SERVER_ERROR,538          err?.message,539        ),540      );541    }542  };543
544  private updatePhone = async (545    req: Request,546    res: Response,547    next: NextFunction,548  ): Promise<Response | void> => {549    try {550      const {phone, password} = req.body;551      const {id} = req.params;552
553      const user = await this.userService.findByIdWithPassword(id);554      if (!user) {555        return next(556          new HttpException(557            ConstantHttpCode.NOT_FOUND,558            ConstantHttpReason.NOT_FOUND,559            ConstantMessage.USER_NOT_FOUND,560          ),561        );562      }563
564      const isPhoneValid = this.validate.validatePhone(phone);565      logger.info({isPhoneValid});566      if (!isPhoneValid) {567        return next(568          new HttpException(569            ConstantHttpCode.BAD_REQUEST,570            ConstantHttpReason.BAD_REQUEST,571            ConstantMessage.PHONE_NOT_VALID,572          ),573        );574      }575
576      const isPasswordValid = this.validate.validatePassword(password);577      if (!isPasswordValid) {578        return next(579          new HttpException(580            ConstantHttpCode.BAD_REQUEST,581            ConstantHttpReason.BAD_REQUEST,582            ConstantMessage.PASSWORD_NOT_VALID,583          ),584        );585      }586
587      const phoneCheck = await this.userService.findByPhone(phone);588      if (phoneCheck) {589        return next(590          new HttpException(591            ConstantHttpCode.NOT_FOUND,592            ConstantHttpReason.NOT_FOUND,593            ConstantMessage.PHONE_EXIST,594          ),595        );596      }597
598      const isMatch = this.userService.comparePassword(password, user.password);599      if (!isMatch) {600        return next(601          new HttpException(602            ConstantHttpCode.UNAUTHORIZED,603            ConstantHttpReason.UNAUTHORIZED,604            ConstantMessage.PASSWORD_NOT_MATCH,605          ),606        );607      }608
609      if (user.phone === phone) {610        return next(611          new HttpException(612            ConstantHttpCode.BAD_REQUEST,613            ConstantHttpReason.BAD_REQUEST,614            ConstantMessage.PHONE_NOT_CHANGE,615          ),616        );617      }618
619      const updatedUser = await this.userService.updatePhone(id, phone);620      if (!updatedUser) {621        return next(622          new HttpException(623            ConstantHttpCode.BAD_REQUEST,624            ConstantHttpReason.BAD_REQUEST,625            ConstantMessage.PHONE_NOT_CHANGE,626          ),627        );628      }629
630      return res.status(ConstantHttpCode.OK).json({631        status: {632          code: ConstantHttpCode.OK,633          msg: ConstantHttpReason.OK,634        },635        msg: ConstantMessage.PHONE_CHANGE_SUCCESS,636        data: {637          user: updatedUser,638        },639      });640    } catch (err: any) {641      next(642        new HttpException(643          ConstantHttpCode.INTERNAL_SERVER_ERROR,644          ConstantHttpReason.INTERNAL_SERVER_ERROR,645          err?.message,646        ),647      );648    }649  };650
651  private updateAddress = async (652    req: Request,653    res: Response,654    next: NextFunction,655  ): Promise<Response | void> => {656    try {657      const {address, password} = req.body;658      const {id} = req.params;659
660      const user = await this.userService.findByIdWithPassword(id);661      if (!user) {662        return next(663          new HttpException(664            ConstantHttpCode.NOT_FOUND,665            ConstantHttpReason.NOT_FOUND,666            ConstantMessage.USER_NOT_FOUND,667          ),668        );669      }670
671      const isAddressValid = this.validate.validateAddress(address);672      if (!isAddressValid) {673        return next(674          new HttpException(675            ConstantHttpCode.BAD_REQUEST,676            ConstantHttpReason.BAD_REQUEST,677            ConstantMessage.ADDRESS_NOT_VALID,678          ),679        );680      }681
682      const isPasswordValid = this.validate.validatePassword(password);683      if (!isPasswordValid) {684        return next(685          new HttpException(686            ConstantHttpCode.BAD_REQUEST,687            ConstantHttpReason.BAD_REQUEST,688            ConstantMessage.PASSWORD_NOT_VALID,689          ),690        );691      }692
693      const isMatch = this.userService.comparePassword(password, user.password);694      if (!isMatch) {695        return next(696          new HttpException(697            ConstantHttpCode.UNAUTHORIZED,698            ConstantHttpReason.UNAUTHORIZED,699            ConstantMessage.PASSWORD_NOT_MATCH,700          ),701        );702      }703
704      if (user.address === address) {705        return next(706          new HttpException(707            ConstantHttpCode.BAD_REQUEST,708            ConstantHttpReason.BAD_REQUEST,709            ConstantMessage.ADDRESS_NOT_CHANGE,710          ),711        );712      }713
714      const updatedUser = await this.userService.updateAddress(id, address);715      if (!updatedUser) {716        return next(717          new HttpException(718            ConstantHttpCode.BAD_REQUEST,719            ConstantHttpReason.BAD_REQUEST,720            ConstantMessage.ADDRESS_NOT_CHANGE,721          ),722        );723      }724
725      return res.status(ConstantHttpCode.OK).json({726        status: {727          code: ConstantHttpCode.OK,728          msg: ConstantHttpReason.OK,729        },730        msg: ConstantMessage.ADDRESS_CHANGE_SUCCESS,731        data: {732          user: updatedUser,733        },734      });735    } catch (err: any) {736      next(737        new HttpException(738          ConstantHttpCode.INTERNAL_SERVER_ERROR,739          ConstantHttpReason.INTERNAL_SERVER_ERROR,740          err?.message,741        ),742      );743    }744  };745
746  private deleteUser = async (747    req: Request,748    res: Response,749    next: NextFunction,750  ): Promise<Response | void> => {751    try {752      const {password} = req.body;753      const {id} = req.params;754
755      const user = await this.userService.findByIdWithPassword(id);756      if (!user) {757        return next(758          new HttpException(759            ConstantHttpCode.NOT_FOUND,760            ConstantHttpReason.NOT_FOUND,761            ConstantMessage.USER_NOT_FOUND,762          ),763        );764      }765
766      const isPasswordValid = this.validate.validatePassword(password);767      if (!isPasswordValid) {768        return next(769          new HttpException(770            ConstantHttpCode.BAD_REQUEST,771            ConstantHttpReason.BAD_REQUEST,772            ConstantMessage.PASSWORD_NOT_VALID,773          ),774        );775      }776
777      const isMatch = this.userService.comparePassword(password, user.password);778      if (!isMatch) {779        return next(780          new HttpException(781            ConstantHttpCode.UNAUTHORIZED,782            ConstantHttpReason.UNAUTHORIZED,783            ConstantMessage.PASSWORD_NOT_MATCH,784          ),785        );786      }787
788      const deletedUser = await this.userService.deleteUser(id);789      if (!deletedUser) {790        return next(791          new HttpException(792            ConstantHttpCode.BAD_REQUEST,793            ConstantHttpReason.BAD_REQUEST,794            ConstantMessage.USER_NOT_DELETE,795          ),796        );797      }798
799      return res.status(ConstantHttpCode.OK).json({800        status: {801          code: ConstantHttpCode.OK,802          msg: ConstantHttpReason.OK,803        },804        msg: ConstantMessage.USER_DELETE_SUCCESS,805      });806    } catch (err: any) {807      next(808        new HttpException(809          ConstantHttpCode.INTERNAL_SERVER_ERROR,810          ConstantHttpReason.INTERNAL_SERVER_ERROR,811          err?.message,812        ),813      );814    }815  };816
817  private getUser = async (818    req: Request,819    res: Response,820    next: NextFunction,821  ): Promise<Response | void> => {822    try {823      const {id} = req.params;824
825      const user = await this.userService.findById(id);826      if (!user) {827        return next(828          new HttpException(829            ConstantHttpCode.NOT_FOUND,830            ConstantHttpReason.NOT_FOUND,831            ConstantMessage.USER_NOT_FOUND,832          ),833        );834      }835
836      return res.status(ConstantHttpCode.OK).json({837        status: {838          code: ConstantHttpCode.OK,839          msg: ConstantHttpReason.OK,840        },841        msg: ConstantMessage.USER_FOUND,842        data: {843          user,844        },845      });846    } catch (err: any) {847      next(848        new HttpException(849          ConstantHttpCode.INTERNAL_SERVER_ERROR,850          ConstantHttpReason.INTERNAL_SERVER_ERROR,851          err?.message,852        ),853      );854    }855  };856
857  private getAllUsers = async (858    _req: Request,859    res: Response,860    next: NextFunction,861  ): Promise<Response | void> => {862    try {863      const users = await this.userService.findAll();864      if (!users) {865        return next(866          new HttpException(867            ConstantHttpCode.NOT_FOUND,868            ConstantHttpReason.NOT_FOUND,869            ConstantMessage.USER_NOT_FOUND,870          ),871        );872      }873
874      return res.status(ConstantHttpCode.OK).json({875        status: {876          code: ConstantHttpCode.OK,877          msg: ConstantHttpReason.OK,878        },879        msg: ConstantMessage.USER_FOUND,880        data: {881          users,882        },883      });884    } catch (err: any) {885      next(886        new HttpException(887          ConstantHttpCode.INTERNAL_SERVER_ERROR,888          ConstantHttpReason.INTERNAL_SERVER_ERROR,889          err?.message,890        ),891      );892    }893  };894
895  private getUsersStats = async (896    _req: Request,897    res: Response,898    next: NextFunction,899  ): Promise<Response | void> => {900    try {901      const usersStats = await this.userService.getUsersStats();902      if (!usersStats) {903        return next(904          new HttpException(905            ConstantHttpCode.NOT_FOUND,906            ConstantHttpReason.NOT_FOUND,907            ConstantMessage.USER_NOT_FOUND,908          ),909        );910      }911
912      return res.status(ConstantHttpCode.OK).json({913        status: {914          code: ConstantHttpCode.OK,915          msg: ConstantHttpReason.OK,916        },917        msg: ConstantMessage.USER_FOUND,918        data: {919          users: usersStats,920        },921      });922    } catch (err: any) {923      next(924        new HttpException(925          ConstantHttpCode.INTERNAL_SERVER_ERROR,926          ConstantHttpReason.INTERNAL_SERVER_ERROR,927          err?.message,928        ),929      );930    }931  };932}933
934export default UserController;
```

`api.constant.ts`

```
1class Api {2  ...3
4  public static readonly AUTH: string = `/auth`5  public static readonly USERS: string = '/users'6}7export default Api
```

after that, we’ll do some changes to the `www.ts` file:

`www.ts`

```
1#!/usr/bin/env ts-node2import App from '..';3// controllers4import AuthController from '@/controllers/auth.controller';5import UserController from '@/controllers/user.controller';6import Variable from '@/env/variable.env';7import logger from '@/utils/logger.util';8import 'core-js/stable';9import http from 'http';10import 'module-alias/register';11import 'regenerator-runtime/runtime';12
13const {app} = new App([new AuthController(), new UserController()]);14
15/**16 * Normalize a port into a number, string, or false.17 */18const normalizePort = (val: any) => {19  const port = parseInt(val, 10);20
21  if (Number.isNaN(port)) {22    // named pipe23    return val;24  }25
26  if (port >= 0) {27    // port number28    return port;29  }30
31  return false;32};33
34const port = normalizePort(Variable.PORT || '3030');35app.set('port', port);36
37/**38 * Create HTTP server.39 */40const server = http.createServer(app);41
42/**43 * Event listener for HTTP server "error" event.44 */45const onError = (error: any) => {46  if (error.syscall !== 'listen') {47    throw error;48  }49
50  const bind = typeof port === 'string' ? `Pipe ${port}` : `Port ${port}`;51
52  // handle specific listen errors with friendly messages53  switch (error.code) {54    case 'EACCES':55      logger.error(`${bind} requires elevated privileges`);56      process.exit(1);57      break;58    case 'EADDRINUSE':59      logger.error(`${bind} is already in use`);60      process.exit(1);61      break;62    default:63      throw error;64  }65};66
67/**68 * Event listener for HTTP server "listening" event.69 */70const onListening = () => {71  const addr = server.address();72  const bind = typeof addr === 'string' ? `pipe ${addr}` : `port ${addr?.port}`;73  logger.info(`Listening on ${bind}`);74};75
76server.listen(port);77server.on('error', onError);78server.on('listening', onListening);
```

## [Summary](#summary)

You learnt about JWTs and how to develop a router-level middleware for JWT authentication in Node.js and Express.js using TypeScript. If we wanted to authenticate all incoming requests to our API, we could also use it as an application-level middleware.

All code from this tutorial as a complete package is available in this [repository](https://github.com/MKAbuMattar/template-express-typescript-blueprint/tree/part2).

## [References](#references)

-   [Setting up Node JS, Express, Prettier, ESLint and Husky Application with Babel and Typescript: Part 1](/post/setting-up-node-js-express-prettier-eslint-and-husky-application-with-babel-and-typescript-part-1)
-   [JSON Web Tokens (JWT) Official Site](https://jwt.io/)
-   [Express.js Official Website](https://expressjs.com/)
-   [MongoDB Official Website](https://www.mongodb.com/)
-   [Mongoose ODM Official Website](https://mongoosejs.com/)
-   [TypeScript Official Website](https://www.typescriptlang.org/)
-   [Babel Official Website](https://babeljs.io/)
-   [ESLint Official Website](https://eslint.org/)
-   [Prettier Official Website](https://prettier.io/)
-   [Husky Official Website](https://typicode.github.io/husky/)
-   [Joi Validation Library (GitHub)](https://github.com/hapijs/joi)
-   [jsonwebtoken (npm package)](https://www.npmjs.com/package/jsonwebtoken)
-   [crypto-js (npm package)](https://www.npmjs.com/package/crypto-js)
-   [Winston Logger (GitHub)](https://github.com/winstonjs/winston)
-   [dotenv (npm package)](https://www.npmjs.com/package/dotenv)
-   [envalid (npm package)](https://www.npmjs.com/package/envalid)
-   [module-alias (npm package)](https://www.npmjs.com/package/module-alias)

Was this useful?

## Tags

[#JWT](/blog/tags/jwt)[#Express.js](/blog/tags/expressjs)[#MongoDB](/blog/tags/mongodb)[#Mongoose](/blog/tags/mongoose)[#Babel](/blog/tags/babel)[#ESLint](/blog/tags/eslint)[#Prettier](/blog/tags/prettier)[#Husky](/blog/tags/husky)[#API Security](/blog/tags/api-security)[#User Authentication](/blog/tags/user-authentication)[#TypeScript Backend](/blog/tags/typescript-backend)[#Joi Validation](/blog/tags/joi-validation)[#Winston Logger](/blog/tags/winston-logger)

## Share

[Facebook](https://facebook.com/sharer/sharer.php?u=https%3A%2F%2Fmkabumattar.com%2Fblog%2Fpost%2Fsetting-up-jwt-authentication-in-typescript-with-express-mongodb-babel-prettier-eslint-and-husky-part-2 "Share on Facebook")[Twitter](https://twitter.com/intent/tweet/?text=Setting%20up%20JWT%20Authentication%20in%20TypeScript%20with%20Express%2C%20MongoDB%2C%20Babel%2C%20Prettier%2C%20ESLint%2C%20and%20Husky%20-%20Part%202&url=https%3A%2F%2Fmkabumattar.com%2Fblog%2Fpost%2Fsetting-up-jwt-authentication-in-typescript-with-express-mongodb-babel-prettier-eslint-and-husky-part-2 "Share on Twitter")[LinkedIn](https://www.linkedin.com/shareArticle?mini=true&url=https%3A%2F%2Fmkabumattar.com%2Fblog%2Fpost%2Fsetting-up-jwt-authentication-in-typescript-with-express-mongodb-babel-prettier-eslint-and-husky-part-2&title=Setting%20up%20JWT%20Authentication%20in%20TypeScript%20with%20Express%2C%20MongoDB%2C%20Babel%2C%20Prettier%2C%20ESLint%2C%20and%20Husky%20-%20Part%202&summary=Setting%20up%20JWT%20Authentication%20in%20Typescript%20with%20Express%2C%20MongoDB%2C%20Babel%2C%20Prettier%2C%20ESLint%2C%20and%20Husky%3A%20Part%202.&source=https://mkabumattar.com "Share on LinkedIn")[WhatsApp](https://wa.me/?text=Setting%20up%20JWT%20Authentication%20in%20TypeScript%20with%20Express%2C%20MongoDB%2C%20Babel%2C%20Prettier%2C%20ESLint%2C%20and%20Husky%20-%20Part%202%20https%3A%2F%2Fmkabumattar.com%2Fblog%2Fpost%2Fsetting-up-jwt-authentication-in-typescript-with-express-mongodb-babel-prettier-eslint-and-husky-part-2 "Share on WhatsApp")[Telegram](https://t.me/share/url?url=https%3A%2F%2Fmkabumattar.com%2Fblog%2Fpost%2Fsetting-up-jwt-authentication-in-typescript-with-express-mongodb-babel-prettier-eslint-and-husky-part-2&text=Setting%20up%20JWT%20Authentication%20in%20TypeScript%20with%20Express%2C%20MongoDB%2C%20Babel%2C%20Prettier%2C%20ESLint%2C%20and%20Husky%20-%20Part%202 "Share on Telegram")[Reddit](https://www.reddit.com/submit?url=https%3A%2F%2Fmkabumattar.com%2Fblog%2Fpost%2Fsetting-up-jwt-authentication-in-typescript-with-express-mongodb-babel-prettier-eslint-and-husky-part-2&title=Setting%20up%20JWT%20Authentication%20in%20TypeScript%20with%20Express%2C%20MongoDB%2C%20Babel%2C%20Prettier%2C%20ESLint%2C%20and%20Husky%20-%20Part%202 "Share on Reddit")[Hacker News](http://news.ycombinator.com/submitlink?u=https%3A%2F%2Fmkabumattar.com%2Fblog%2Fpost%2Fsetting-up-jwt-authentication-in-typescript-with-express-mongodb-babel-prettier-eslint-and-husky-part-2&t=Setting%20up%20JWT%20Authentication%20in%20TypeScript%20with%20Express%2C%20MongoDB%2C%20Babel%2C%20Prettier%2C%20ESLint%2C%20and%20Husky%20-%20Part%202 "Share on Hacker News")[Pinterest](https://pinterest.com/pin/create/button/?url=https%3A%2F%2Fmkabumattar.com%2Fblog%2Fpost%2Fsetting-up-jwt-authentication-in-typescript-with-express-mongodb-babel-prettier-eslint-and-husky-part-2&media=&description=Setting%20up%20JWT%20Authentication%20in%20Typescript%20with%20Express%2C%20MongoDB%2C%20Babel%2C%20Prettier%2C%20ESLint%2C%20and%20Husky%3A%20Part%202. "Share on Pinterest")[Email](<mailto:?subject=Setting%20up%20JWT%20Authentication%20in%20TypeScript%20with%20Express%2C%20MongoDB%2C%20Babel%2C%20Prettier%2C%20ESLint%2C%20and%20Husky%20-%20Part%202&body=Check out this article: https%3A%2F%2Fmkabumattar.com%2Fblog%2Fpost%2Fsetting-up-jwt-authentication-in-typescript-with-express-mongodb-babel-prettier-eslint-and-husky-part-2>)

## Comments

## You might also enjoy

More posts on similar topics

[![Setting up Node.js, Express, Prettier, ESLint, and Husky application with Babel and TypeScript - Part 1](/_astro/hero.DKzl3k6w_w3X8j.webp)](/blog/post/setting-up-node-js-express-prettier-eslint-and-husky-application-with-babel-and-typescript-part-1)

## [Setting up Node.js, Express, Prettier, ESLint, and Husky application with Babel and TypeScript - Part 1](/blog/post/setting-up-node-js-express-prettier-eslint-and-husky-application-with-babel-and-typescript-part-1)

-   [Mohammad Abu Mattar](/authors/mohammad-abu-mattar)
-   [Backend Development](/blog/categories/backend-development)
-   [Node.js](/blog/categories/nodejs)
-   [TypeScript](/blog/categories/typescript)
-   [Development Setup](/blog/categories/development-setup)
-   [JavaScript Tooling](/blog/categories/javascript-tooling)

Introduction All code from this tutorial as a complete package is available in this repos

[#Node.js](/blog/tags/nodejs)[#Express.js](/blog/tags/expressjs)[#TypeScript](/blog/tags/typescript)+9 tags

[read more](/blog/post/setting-up-node-js-express-prettier-eslint-and-husky-application-with-babel-and-typescript-part-1)

[![Setting up Node JS, Express, MongoDB, Prettier, ESLint and Husky Application with Babel and authentication as an example](/_astro/hero.C-0XrT7F_1bXKFs.webp)](/blog/post/setting-up-node-js-express-mongodb-prettier-eslint-and-husky-application-with-babel-and-authentication-as-an-example)

## [Setting up Node JS, Express, MongoDB, Prettier, ESLint and Husky Application with Babel and authentication as an example](/blog/post/setting-up-node-js-express-mongodb-prettier-eslint-and-husky-application-with-babel-and-authentication-as-an-example)

-   [Mohammad Abu Mattar](/authors/mohammad-abu-mattar)
-   [Backend Development](/blog/categories/backend-development)
-   [Node.js](/blog/categories/nodejs)
-   [JavaScript](/blog/categories/javascript)
-   [Development Setup](/blog/categories/development-setup)
-   [API Development](/blog/categories/api-development)

Introduction All code from this tutorial as a complete package is available in this repository. If you find this tutorial helpful, please share i

[#Node.js](/blog/tags/nodejs)[#Express.js](/blog/tags/expressjs)[#MongoDB](/blog/tags/mongodb)+11 tags

[read more](/blog/post/setting-up-node-js-express-mongodb-prettier-eslint-and-husky-application-with-babel-and-authentication-as-an-example)

[![Caching Strategies with Redis in Node.js and TypeScript](/_astro/hero.KUKAT2kl_Z1Ypf5o.webp)](/blog/post/caching-strategies-with-redis-in-node-js-and-typescript)

## [Caching Strategies with Redis in Node.js and TypeScript](/blog/post/caching-strategies-with-redis-in-node-js-and-typescript)

-   [Mohammad Abu Mattar](/authors/mohammad-abu-mattar)
-   [Backend Development](/blog/categories/backend-development)
-   [Caching](/blog/categories/caching)
-   [Performance Optimization](/blog/categories/performance-optimization)
-   [Node.js](/blog/categories/nodejs)
-   [Redis](/blog/categories/redis)
-   [TypeScript](/blog/categories/typescript)

Introduction Optimizing application performance is an ongoing job, and caching is one of the most effective ways to do it. Redis, a fast in-memory data store, is a common choice for caching in Nod

[#Redis Cache](/blog/tags/redis-cache)[#Caching Strategies](/blog/tags/caching-strategies)[#Node.js Performance](/blog/tags/nodejs-performance)+8 tags

[read more](/blog/post/caching-strategies-with-redis-in-node-js-and-typescript)

[![The ORM Dilemma: To Use or Not to Use](/_astro/hero.DwvXnAvK_Zoy6I4.webp)](/blog/post/why-not-to-use-orm-in-nodejs)

## [The ORM Dilemma: To Use or Not to Use](/blog/post/why-not-to-use-orm-in-nodejs)

-   [Mohammad Abu Mattar](/authors/mohammad-abu-mattar)
-   [Backend Development](/blog/categories/backend-development)
-   [Node.js](/blog/categories/nodejs)
-   [Database Management](/blog/categories/database-management)
-   [Software Architecture](/blog/categories/software-architecture)
-   [TypeScript](/blog/categories/typescript)

Introduction Some decisions shape a project more than others. One that keeps coming back is whether to use an Object-Relational Mapping (ORM) tool for database interactions. Should you skip an ORM

[#ORM](/blog/tags/orm)[#Node.js](/blog/tags/nodejs)[#TypeScript](/blog/tags/typescript)+8 tags

[read more](/blog/post/why-not-to-use-orm-in-nodejs)

[![Run TypeScript Without Compiling](/_astro/hero.EI1J4T1U_ZTF3Kf.webp)](/blog/post/run-typescript-without-compiling)

## [Run TypeScript Without Compiling](/blog/post/run-typescript-without-compiling)

-   [Mohammad Abu Mattar](/authors/mohammad-abu-mattar)
-   [TypeScript](/blog/categories/typescript)
-   [Node.js](/blog/categories/nodejs)
-   [JavaScript](/blog/categories/javascript)
-   [Development Tools](/blog/categories/development-tools)

Introduction In this post, I will show you how to run TypeScript without compiling it to JavaScript first. This is useful for debugging and testing. Set up a TypeScript project Step 1: cr

[#TypeScript](/blog/tags/typescript)[#Node.js](/blog/tags/nodejs)[#Ts node](/blog/tags/ts-node)+6 tags

[read more](/blog/post/run-typescript-without-compiling)

[![REST API vs RESTful API: Architecture and Constraints Explained](/_astro/hero.D7ffsaFk_ZsjslT.webp)](/blog/post/rest-api-vs-restful-api-architecture-and-constraints-explained)

## [REST API vs RESTful API: Architecture and Constraints Explained](/blog/post/rest-api-vs-restful-api-architecture-and-constraints-explained)

-   [Mohammad Abu Mattar](/authors/mohammad-abu-mattar)
-   [API Development](/blog/categories/api-development)
-   [Web Architecture](/blog/categories/web-architecture)
-   [Software Engineering](/blog/categories/software-engineering)
-   [Backend Development](/blog/categories/backend-development)

Introduction REST API and RESTful API get used interchangeably, but they aren't quite the same thing. This post covers the difference, REST's constraints, and what they mean for how you design an

[#REST API](/blog/tags/rest-api)[#RESTful API](/blog/tags/restful-api)[#API Design Principles](/blog/tags/api-design-principles)+6 tags

[read more](/blog/post/rest-api-vs-restful-api-architecture-and-constraints-explained)

6 related posts
