---
title: "Setting up Node JS, Express, MongoDB, Prettier, ESLint and Husky Application with Babel and authentication as an example"
lang: "en"
author: "Mohammad Abu Mattar"
canonical: https://mkabumattar.com/post/setting-up-node-js-express-mongodb-prettier-eslint-and-husky-application-with-babel-and-authentication-as-an-example
---

![Blog post image for 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.](/_astro/hero.C-0XrT7F_NW0rK.webp)

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

Blog

[Prev in Backend DevelopmentSetting up JWT Authentication in TypeScript with Express, MongoDB, Babel, Prettier, ESLint, and Husky - Part 2](/blog/post/setting-up-jwt-authentication-in-typescript-with-express-mongodb-babel-prettier-eslint-and-husky-part-2)[Next in Backend DevelopmentSetting 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)

[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)

# 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)Published: 25 Jun 2022Updated: 08 Jun 202628 Mins read22 Mins listen

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

TL;DR

Setting up Node JS, Express, MongoDB, Prettier, ESLint and Husky Application with Babel and authentication as an example.

Series

[Backend Programming](/series/backend-programming)1/3

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

All posts in this series (3)

Blog3

1.  [Setting up Node JS, Express, MongoDB, Prettier, ESLint and Husky Application with Babel and authentication as an exampleYou are here](/blog/post/setting-up-node-js-express-mongodb-prettier-eslint-and-husky-application-with-babel-and-authentication-as-an-example)
2.  [Caching Strategies with Redis in Node.js and TypeScript](/blog/post/caching-strategies-with-redis-in-node-js-and-typescript)
3.  [The ORM Dilemma: To Use or Not to Use](/blog/post/why-not-to-use-orm-in-nodejs)

### Setting up Node JS, Express, MongoDB, Prettier, ESLint and Husky Application with Babel and authentication as an example

Contents

[Introduction](#introduction)[What is Babel?](#what-is-babel)[Project setup](#project-setup)[Engine locking](#engine-locking)[Babel setup](#babel-setup)[compression cookie-parser core-js crypto-js helmet jsonwebtoken lodash regenerator-runtime](#compression-cookie-parser-core-js-crypto-js-helmet-jsonwebtoken-lodash-regenerator-runtime)[\# Babel configuration](#-babel-configuration)[Code formatting and quality tools](#code-formatting-and-quality-tools)[\# Prettier](#-prettier)[\# ESLint](#-eslint)[Setup logger for development](#setup-logger-for-development)[Build file structure and basic express application](#build-file-structure-and-basic-express-application)[Git hooks](#git-hooks)[Husky](#husky)[Commitlint](#commitlint)[VS Code](#vs-code)[Configuration](#configuration)[Debugging](#debugging)[Authentication](#authentication)[Authentication setup](#authentication-setup)[Authentication middleware](#authentication-middleware)[Authentication security](#authentication-security)[Authentication validations](#authentication-validations)[Authentication schemas](#authentication-schemas)[Authentication models](#authentication-models)[Authentication repositories](#authentication-repositories)[Authentication services](#authentication-services)[Authentication controllers](#authentication-controllers)[Authentication routes](#authentication-routes)[Frequently asked questions](#frequently-asked-questions)[Summary](#summary)[References](#references)

## [Introduction](#introduction)

All code from this tutorial as a complete package is available in this [repository](https://github.com/MKAbuMattar/template-express). If you find this tutorial helpful, please share it with your friends and colleagues, and make sure to star the repository.

Since the ECMAScript JavaScript Standard is revised annually, it is a good idea to update our code as well.

The most recent JavaScript standards are occasionally incompatible with the browser. Something like Babel, which is nothing more than a JavaScript transpiler, is what we need to fix this sort of issue.

So, in this little tutorial, I’ll explain how to set up babel for a basic NodeJS Express application so that we may use the most recent ES6 syntax in it, then layer on MongoDB, a Winston logger, Prettier, ESLint, Husky git hooks, and JWT authentication.

Worth knowing

By the end you’ll have a production-ready Express API scaffold: ES module syntax via Babel, MongoDB through Mongoose, structured logging, enforced formatting and linting, git hooks that block bad commits, and a complete register/login flow secured with JSON Web Tokens.

You’ll push the finished project to a remote over SSH, so if you haven’t set up your keys yet, see [Git SSH Keys for GitHub, GitLab, and Bitbucket on Linux](/blog/post/git-ssh-keys-for-github-gitlab-and-bitbucket-on-linux).

## [What is Babel?](#what-is-babel)

Babel is a toolchain that is mainly used to convert ECMAScript 2015+ code into a backwards compatible version of JavaScript in current and older browsers or environments. Here are the main things Babel can do for you:

-   Transform syntax
-   Polyfill features that are missing in your target environment (through a third-party polyfill such as core-js)
-   Source code transformations (codemods)

## [Project setup](#project-setup)

We’ll begin by creating a new directory called `backend-template` and then we’ll create a new `package.json` file. We’re going to be using pnpm for this example, but you could just as easily use npm or yarn if you prefer. pnpm is fast and disk-efficient thanks to its content-addressable store.

Create the project directory

```
1mkdir backend-template2cd backend-template3pnpm init
```

### [Engine locking](#engine-locking)

The same Node engine and package management that we use should be available to all developers working on this project. We create two new files to achieve that:

-   `.nvmrc`: Will disclose to other project users the Node version that is being used.
-   `.npmrc`: reveals to other project users the package manager being used.

Create the .nvmrc files

```
1touch .nvmrc2echo "lts/fermium" > .nvmrc
```

Create the .npmrc files

```
1touch .npmrc2echo 'engine-strict=true\r\nsave-exact = true\r\ntag-version-prefix=""\r\nstrict-peer-dependencies = false\r\nauto-install-peers = true\r\nlockfile = true' > .npmrc
```

With pnpm, repo-wide install settings live in a `pnpm-workspace.yaml` file at the project root, something like this:

pnpm-workspace.yaml

```
1# Only run install/build scripts for packages you trust to build native binaries.2onlyBuiltDependencies:3  - esbuild4
5# Pin a single resolution for a dependency across the whole tree.6overrides:7  # 'some-transitive-pkg': 1.2.38
9# Optionally defer adopting brand-new releases (supply-chain safety).10# minimumReleaseAge: 1440
```

Notably, the usage of `engine-strict` said nothing about `pnpm` in particular; we handle that in `packages.json`:

open `packages.json` add the `engines`:

packages.json

```
1{2  "name": "tutorial",3  "version": "0.0.0",4  "description": "",5  "keywords": [],6  "main": "index.js",7  "license": "MIT",8  "author": {9    "name": "Mohammad Abu Mattar",10    "email": "mohammad.abumattar@outlook.com",11    "url": "https://mkabumattar.github.io/"12  },13  "homepage": "YOUR_GIT_REPO_URL#readme",14  "repository": {15    "type": "git",16    "url": "git+YOUR_GIT_REPO_URL.git"17  },18  "bugs": {19    "url": "YOUR_GIT_REPO_URL/issues"20  },21  "engines": {22    "node": ">=14.0.0",23    "pnpm": ">=8.0.0"24  }25}
```

### [Babel setup](#babel-setup)

For the production build we install two main Babel packages.

-   _`@babel/core`_: The primary package for running any Babel setup or configuration.
-   _`@babel/preset-env`_: Gives us access to modern JavaScript features and transpiles them down to a version the target Node.js understands.

Babel is mostly used in the code base to take advantage of new JavaScript capabilities. Unless the code is pure JavaScript, we don’t know if the server’s Node.js will comprehend the specific code or not, so transpiling before deployment is advised, and that’s exactly what `@babel/cli` does in the `build` script.

Tip

For development we don’t want to transpile to disk on every save. Instead of `nodemon` + `babel-node`, we’ll use [tsx](https://www.npmjs.com/package/tsx), a tiny Node.js runner powered by esbuild that executes modern ES module `.js` (and TypeScript) directly and ships a `--watch` mode, so it replaces **both** `nodemon` and `babel-node` with one dependency.

Development Setup:

Install tsx for development

```
1pnpm add -D tsx
```

Install the babel packages

```
1pnpm add express mongoose cors dotenv @babel/core @babel/preset-env2
3##  compression cookie-parser core-js crypto-js helmet jsonwebtoken lodash regenerator-runtime
```

Install the babel packages

```
1pnpm add -D @babel/cli babel-plugin-module-resolver
```

Here, we initialize the package.json and install the basic Express server, mongoose, cors, dotenv, `@babel/core` and `@babel/preset-env` (for the production build), plus the dev tooling: `tsx`, `@babel/cli`, and `babel-plugin-module-resolver`.

#### [Babel configuration](#babel-configuration)

After that, we need to create a file called `.babelrc` in the project’s root directory, and we paste the following block of code there.

Create the .babelrc file

```
1touch .babelrc
```

.babelrc

```
1{2  "presets": ["@babel/preset-env"]3}
```

### [Code formatting and quality tools](#code-formatting-and-quality-tools)

We will be using two tools to establish a standard that all project participants will follow, so the coding style and the use of fundamental best practices stay consistent:

-   [Prettier](https://prettier.io/): A tool that will help us to format our code consistently.
-   [ESLint](https://eslint.org/): A tool that will help us to enforce a consistent coding style.

#### [Prettier](#prettier)

Prettier will handle the automated file formatting for us. Add it to the project right now.

It’s only needed during development, so I’ll add it as a `devDependency` with `-D`

Install Prettier

```
1pnpm add -D prettier
```

Tip

Install the [Prettier VS Code extension](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode) so the editor formats on save instead of you running the CLI by hand. Keep Prettier in the project’s dependencies anyway. VS Code uses your project’s local config and version.

We’ll create two files in the root:

-   `.prettierrc`: This file will contain the configuration for prettier.
-   `.prettierignore`: This file will contain the list of files that should be ignored by prettier.

.prettierrc

```
1{2  "trailingComma": "all",3  "printWidth": 80,4  "tabWidth": 2,5  "useTabs": false,6  "semi": false,7  "singleQuote": true8}
```

.prettierignore

```
1node_modules2build
```

I’ve listed the folders in that file that I don’t want Prettier to waste any time working on. If you’d want to disregard specific file types in groups, you may also use patterns like \*.html.

Now we add a new script to `package.json` so we can run Prettier:

package.json

```
6 collapsed lines1{2  "name": "tutorial",3  "version": "0.0.0",4  "description": "",5  "keywords": [],6  "main": "index.js",7  "scripts": {6 collapsed lines8    "start": "node build/bin/www.js",9    "dev": "tsx watch src/bin/www.js",10    "clean": "rm -rf build",11    "build": "pnpm clean && pnpm exec babel src -d build --minified --presets @babel/preset-env",12    "lint": "eslint \"src/**/*.js\" --fix",13    "lint:check": "eslint \"src/**/*.js\"",14    "prettier": "prettier --write \"src/**/*.js\"",15    "prettier:check": "prettier --check \"src/**/*.js\"",16    "prepare": "husky install"17  },49 collapsed lines18  "license": "MIT",19  "author": {20    "name": "YOUR_NAME",21    "email": "YOUR_EMAIL",22    "url": "YOUR_WEBSITE"23  },24  "homepage": "YOUR_GIT_REPO_URL#readme",25  "repository": {26    "type": "git",27    "url": "git+YOUR_GIT_REPO_URL.git"28  },29  "bugs": {30    "url": "YOUR_GIT_REPO_URL/issues"31  },32  "engines": {33    "node": ">=14.0.0",34    "pnpm": ">=8.0.0"35  },36  "dependencies": {37    "@babel/core": "7.18.5",38    "@babel/preset-env": "7.18.2",39    "compression": "1.7.4",40    "cookie-parser": "1.4.6",41    "core-js": "3.23.2",42    "cors": "2.8.5",43    "crypto-js": "4.1.1",44    "dotenv": "16.0.1",45    "express": "4.18.1",46    "helmet": "5.1.0",47    "husky": "8.0.1",48    "jsonwebtoken": "9.0.0",49    "mongoose": "6.11.3",50    "regenerator-runtime": "0.13.9",51    "winston": "3.8.0"52  },53  "devDependencies": {54    "@babel/cli": "7.17.10",55    "@commitlint/cli": "17.0.3",56    "@commitlint/config-conventional": "17.0.3",57    "babel-plugin-module-resolver": "4.1.0",58    "eslint": "8.18.0",59    "eslint-config-airbnb-base": "15.0.0",60    "eslint-config-prettier": "8.5.0",61    "eslint-plugin-import": "2.26.0",62    "eslint-plugin-prettier": "4.0.0",63    "prettier": "2.7.1",64    "tsx": "4.19.2"65  }66}
```

You can now run `pnpm prettier` to format all files in the project, or `pnpm prettier:check` to check if all files are formatted correctly.

Run Prettier

```
1pnpm prettier:check2pnpm prettier
```

to automatically format, repair, and save all files in your project that you haven’t ignored. My formatter updated around 7 files by default. The source control tab on the left of VS Code has a list of altered files where you may find them.

#### [ESLint](#eslint)

We’ll begin with ESLint, a tool that will help us enforce a consistent coding style. First we need to install the dependencies.

Install ESLint

```
1pnpm add -D eslint eslint-config-airbnb-base eslint-config-prettier eslint-plugin-import eslint-plugin-prettier
```

We’ll create two files in the root:

-   `.eslintrc`: This file will contain the configuration for ESLint.
-   `.eslintignore`: This file will contain the list of files that should be ignored by ESLint.

.eslintrc

```
1{2  "extends": [3    "airbnb-base",4    "plugin:prettier/recommended",5    "plugin:import/errors",6    "plugin:import/warnings"7  ],8  "plugins": ["prettier"],9  "rules": {10    "prettier/prettier": "error",11    "import/no-named-as-default": "off",12    "no-underscore-dangle": "off"13  }14}
```

.eslintignore

```
1node_modules2build
```

Now we add a new script to `package.json` so we can run ESLint:

package.json

```
6 collapsed lines1{2  "name": "tutorial",3  "version": "0.0.0",4  "description": "",5  "keywords": [],6  "main": "index.js",7  "scripts": {4 collapsed lines8    "start": "node build/bin/www.js",9    "dev": "tsx watch src/bin/www.js",10    "clean": "rm -rf build",11    "build": "pnpm clean && pnpm exec babel src -d build --minified --presets @babel/preset-env",12    "lint": "eslint \"src/**/*.js\" --fix",13    "lint:check": "eslint \"src/**/*.js\"",3 collapsed lines14    "prettier": "prettier --write \"src/**/*.js\"",15    "prettier:check": "prettier --check \"src/**/*.js\"",16    "prepare": "husky install"17  },49 collapsed lines18  "license": "MIT",19  "author": {20    "name": "YOUR_NAME",21    "email": "YOUR_EMAIL",22    "url": "YOUR_WEBSITE"23  },24  "homepage": "YOUR_GIT_REPO_URL#readme",25  "repository": {26    "type": "git",27    "url": "git+YOUR_GIT_REPO_URL.git"28  },29  "bugs": {30    "url": "YOUR_GIT_REPO_URL/issues"31  },32  "engines": {33    "node": ">=14.0.0",34    "pnpm": ">=8.0.0"35  },36  "dependencies": {37    "@babel/core": "7.18.5",38    "@babel/preset-env": "7.18.2",39    "compression": "1.7.4",40    "cookie-parser": "1.4.6",41    "core-js": "3.23.2",42    "cors": "2.8.5",43    "crypto-js": "4.1.1",44    "dotenv": "16.0.1",45    "express": "4.18.1",46    "helmet": "5.1.0",47    "husky": "8.0.1",48    "jsonwebtoken": "9.0.0",49    "mongoose": "6.11.3",50    "regenerator-runtime": "0.13.9",51    "winston": "3.8.0"52  },53  "devDependencies": {54    "@babel/cli": "7.17.10",55    "@commitlint/cli": "17.0.3",56    "@commitlint/config-conventional": "17.0.3",57    "babel-plugin-module-resolver": "4.1.0",58    "eslint": "8.18.0",59    "eslint-config-airbnb-base": "15.0.0",60    "eslint-config-prettier": "8.5.0",61    "eslint-plugin-import": "2.26.0",62    "eslint-plugin-prettier": "4.0.0",63    "prettier": "2.7.1",64    "tsx": "4.19.2"65  }66}
```

Now you can test out your config.

You can run `pnpm lint` to format all files in the project, or `pnpm lint:check` to check if all files are formatted correctly.

Run ESLint

```
1pnpm lint:check2pnpm lint
```

### [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 can log incoming requests by specifying the formatting of log instance based on different request related information.
-   [Winston](https://www.npmjs.com/package/winston): A lightweight but effective logging library that supports multiple types of transports. Because I want to simultaneously log events into a file and a terminal, this practical feature matters to me.

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

Install Winston

```
1pnpm add winston
```

Then I’ll create a file called `utils/logger.util.js` in it:

Create the logger.util.js file

```
1touch src/utils/logger.util.js
```

src/utils/logger.util.js

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

### [Build file structure and basic express application](#build-file-structure-and-basic-express-application)

at first we’ll create a directory called `src`, and we’ll build the file structure inside of it.

Create the src directory

```
1mkdir src2cd src3
4mkdir bin config constants controllers env middlewares models repositories routers schemas security services validations
```

This is the directory layout we’re building inside `src`:

-   Directorysrc/
    
    -   Directorybin/ server bootstrap ([www.js](http://www.js))
        
        -   …
        
    -   Directoryconfig/ database connection
        
        -   …
        
    -   Directoryconstants/ shared constants (HTTP codes, messages, paths)
        
        -   …
        
    -   Directorycontrollers/ request handlers
        
        -   …
        
    -   Directoryenv/ environment-variable loading
        
        -   …
        
    -   Directorymiddlewares/ auth and error middleware
        
        -   …
        
    -   Directorymodels/ Mongoose models
        
        -   …
        
    -   Directoryrepositories/ data-access layer
        
        -   …
        
    -   Directoryrouters/ route definitions
        
        -   …
        
    -   Directoryschemas/ Mongoose schemas
        
        -   …
        
    -   Directorysecurity/ hashing and token helpers
        
        -   …
        
    -   Directoryservices/ business logic
        
        -   …
        
    -   Directoryvalidations/ request validation
        
        -   …
        
    

To keep things maintainable and avoid repeating text, we’ll create constants files in the `constants` directory:

Create the constants directory

```
1touch src/constants/api.constant.js src/constants/dateformat.constant.js src/constants/http.code.constant.js src/constants/http.reason.constant.js src/constants/message.constant.js src/constants/model.constant.js src/constants/number.constant.js src/constants/path.constant.js src/constants/regex.constant.js
```

there are some constants that are used in the application, but that are not related to the application itself. For example, the constants that are used in the API are in the `api.constant.js` file, and there is a file that provides HTTP codes and replies for free, but we’ll make our own from scratch.

src/constants/dateformat.constant.js

```
1export const YYYY_MM_DD_HH_MM_SS_MS = 'YYYY-MM-DD HH:mm:ss:ms';2
3export default {4  YYYY_MM_DD_HH_MM_SS_MS,5};
```

src/constants/http.code.constant.js

```
1export const CONTINUE = 100;2export const SWITCHING_PROTOCOLS = 101;3export const PROCESSING = 102;4export const OK = 200;5export const CREATED = 201;6export const ACCEPTED = 202;7export const NON_AUTHORITATIVE_INFORMATION = 203;8export const NO_CONTENT = 204;9export const RESET_CONTENT = 205;10export const PARTIAL_CONTENT = 206;11export const MULTI_STATUS = 207;12export const ALREADY_REPORTED = 208;13export const IM_USED = 226;14export const MULTIPLE_CHOICES = 300;15export const MOVED_PERMANENTLY = 301;16export const MOVED_TEMPORARILY = 302;17export const SEE_OTHER = 303;18export const NOT_MODIFIED = 304;19export const USE_PROXY = 305;20export const SWITCH_PROXY = 306;21export const TEMPORARY_REDIRECT = 307;22export const BAD_REQUEST = 400;23export const UNAUTHORIZED = 401;24export const PAYMENT_REQUIRED = 402;25export const FORBIDDEN = 403;26export const NOT_FOUND = 404;27export const METHOD_NOT_ALLOWED = 405;28export const NOT_ACCEPTABLE = 406;29export const PROXY_AUTHENTICATION_REQUIRED = 407;30export const REQUEST_TIMEOUT = 408;31export const CONFLICT = 409;32export const GONE = 410;33export const LENGTH_REQUIRED = 411;34export const PRECONDITION_FAILED = 412;35export const PAYLOAD_TOO_LARGE = 413;36export const REQUEST_URI_TOO_LONG = 414;37export const UNSUPPORTED_MEDIA_TYPE = 415;38export const REQUESTED_RANGE_NOT_SATISFIABLE = 416;39export const EXPECTATION_FAILED = 417;40export const IM_A_TEAPOT = 418;41export const METHOD_FAILURE = 420;42export const MISDIRECTED_REQUEST = 421;43export const UNPROCESSABLE_ENTITY = 422;44export const LOCKED = 423;45export const FAILED_DEPENDENCY = 424;46export const UPGRADE_REQUIRED = 426;47export const PRECONDITION_REQUIRED = 428;48export const TOO_MANY_REQUESTS = 429;49export const REQUEST_HEADER_FIELDS_TOO_LARGE = 431;50export const UNAVAILABLE_FOR_LEGAL_REASONS = 451;51export const INTERNAL_SERVER_ERROR = 500;52export const NOT_IMPLEMENTED = 501;53export const BAD_GATEWAY = 502;54export const SERVICE_UNAVAILABLE = 503;55export const GATEWAY_TIMEOUT = 504;56export const HTTP_VERSION_NOT_SUPPORTED = 505;57export const VARIANT_ALSO_NEGOTIATES = 506;58export const INSUFFICIENT_STORAGE = 507;59export const LOOP_DETECTED = 508;60export const NOT_EXTENDED = 510;61export const NETWORK_AUTHENTICATION_REQUIRED = 511;62export const NETWORK_CONNECT_TIMEOUT_ERROR = 599;63
64export default {65  CONTINUE,66  SWITCHING_PROTOCOLS,67  PROCESSING,68  OK,69  CREATED,70  ACCEPTED,71  NON_AUTHORITATIVE_INFORMATION,72  NO_CONTENT,73  RESET_CONTENT,74  PARTIAL_CONTENT,75  MULTI_STATUS,76  ALREADY_REPORTED,77  IM_USED,78  MULTIPLE_CHOICES,79  MOVED_PERMANENTLY,80  MOVED_TEMPORARILY,81  SEE_OTHER,82  NOT_MODIFIED,83  USE_PROXY,84  SWITCH_PROXY,85  TEMPORARY_REDIRECT,86  BAD_REQUEST,87  UNAUTHORIZED,88  PAYMENT_REQUIRED,89  FORBIDDEN,90  NOT_FOUND,91  METHOD_NOT_ALLOWED,92  NOT_ACCEPTABLE,93  PROXY_AUTHENTICATION_REQUIRED,94  REQUEST_TIMEOUT,95  CONFLICT,96  GONE,97  LENGTH_REQUIRED,98  PRECONDITION_FAILED,99  PAYLOAD_TOO_LARGE,100  REQUEST_URI_TOO_LONG,101  UNSUPPORTED_MEDIA_TYPE,102  REQUESTED_RANGE_NOT_SATISFIABLE,103  EXPECTATION_FAILED,104  IM_A_TEAPOT,105  METHOD_FAILURE,106  MISDIRECTED_REQUEST,107  UNPROCESSABLE_ENTITY,108  LOCKED,109  FAILED_DEPENDENCY,110  UPGRADE_REQUIRED,111  PRECONDITION_REQUIRED,112  TOO_MANY_REQUESTS,113  REQUEST_HEADER_FIELDS_TOO_LARGE,114  UNAVAILABLE_FOR_LEGAL_REASONS,115  INTERNAL_SERVER_ERROR,116  NOT_IMPLEMENTED,117  BAD_GATEWAY,118  SERVICE_UNAVAILABLE,119  GATEWAY_TIMEOUT,120  HTTP_VERSION_NOT_SUPPORTED,121  VARIANT_ALSO_NEGOTIATES,122  INSUFFICIENT_STORAGE,123  LOOP_DETECTED,124  NOT_EXTENDED,125  NETWORK_AUTHENTICATION_REQUIRED,126  NETWORK_CONNECT_TIMEOUT_ERROR,127};
```

src/constants/http.reason.constant.js

```
1export const CONTINUE = 'Continue';2export const SWITCHING_PROTOCOLS = 'Switching Protocols';3export const PROCESSING = 'Processing';4export const OK = 'OK';5export const CREATED = 'Created';6export const ACCEPTED = 'Accepted';7export const NON_AUTHORITATIVE_INFORMATION = 'Non-Authoritative Information';8export const NO_CONTENT = 'No Content';9export const RESET_CONTENT = 'Reset Content';10export const PARTIAL_CONTENT = 'Partial Content';11export const MULTI_STATUS = 'Multi-Status';12export const ALREADY_REPORTED = 'Already Reported';13export const IM_USED = 'IM Used';14export const MULTIPLE_CHOICES = 'Multiple Choices';15export const MOVED_PERMANENTLY = 'Moved Permanently';16export const MOVED_TEMPORARILY = 'Moved Temporarily';17export const SEE_OTHER = 'See Other';18export const NOT_MODIFIED = 'Not Modified';19export const USE_PROXY = 'Use Proxy';20export const SWITCH_PROXY = 'Switch Proxy';21export const TEMPORARY_REDIRECT = 'Temporary Redirect';22export const BAD_REQUEST = 'Bad Request';23export const UNAUTHORIZED = 'Unauthorized';24export const PAYMENT_REQUIRED = 'Payment Required';25export const FORBIDDEN = 'Forbidden';26export const NOT_FOUND = 'Not Found';27export const METHOD_NOT_ALLOWED = 'Method Not Allowed';28export const NOT_ACCEPTABLE = 'Not Acceptable';29export const PROXY_AUTHENTICATION_REQUIRED = 'Proxy Authentication Required';30export const REQUEST_TIMEOUT = 'Request Timeout';31export const CONFLICT = 'Conflict';32export const GONE = 'Gone';33export const LENGTH_REQUIRED = 'Length Required';34export const PRECONDITION_FAILED = 'Precondition Failed';35export const PAYLOAD_TOO_LARGE = 'Payload Too Large';36export const REQUEST_URI_TOO_LONG = 'Request URI Too Long';37export const UNSUPPORTED_MEDIA_TYPE = 'Unsupported Media Type';38export const REQUESTED_RANGE_NOT_SATISFIABLE =39  'Requested Range Not Satisfiable';40export const EXPECTATION_FAILED = 'Expectation Failed';41export const IM_A_TEAPOT = "I'm a teapot";42export const METHOD_FAILURE = 'Method Failure';43export const MISDIRECTED_REQUEST = 'Misdirected Request';44export const UNPROCESSABLE_ENTITY = 'Unprocessable Entity';45export const LOCKED = 'Locked';46export const FAILED_DEPENDENCY = 'Failed Dependency';47export const UPGRADE_REQUIRED = 'Upgrade Required';48export const PRECONDITION_REQUIRED = 'Precondition Required';49export const TOO_MANY_REQUESTS = 'Too Many Requests';50export const REQUEST_HEADER_FIELDS_TOO_LARGE =51  'Request Header Fields Too Large';52export const UNAVAILABLE_FOR_LEGAL_REASONS = 'Unavailable For Legal Reasons';53export const INTERNAL_SERVER_ERROR = 'Internal Server Error';54export const NOT_IMPLEMENTED = 'Not Implemented';55export const BAD_GATEWAY = 'Bad Gateway';56export const SERVICE_UNAVAILABLE = 'Service Unavailable';57export const GATEWAY_TIMEOUT = 'Gateway Timeout';58export const HTTP_VERSION_NOT_SUPPORTED = 'HTTP Version Not Supported';59export const VARIANT_ALSO_NEGOTIATES = 'Variant Also Negotiates';60export const INSUFFICIENT_STORAGE = 'Insufficient Storage';61export const LOOP_DETECTED = 'Loop Detected';62export const NOT_EXTENDED = 'Not Extended';63export const NETWORK_AUTHENTICATION_REQUIRED =64  'Network Authentication Required';65export const NETWORK_CONNECT_TIMEOUT_ERROR = 'Network Connect Timeout Error';66
67export default {68  CONTINUE,69  SWITCHING_PROTOCOLS,70  PROCESSING,71  OK,72  CREATED,73  ACCEPTED,74  NON_AUTHORITATIVE_INFORMATION,75  NO_CONTENT,76  RESET_CONTENT,77  PARTIAL_CONTENT,78  MULTI_STATUS,79  ALREADY_REPORTED,80  IM_USED,81  MULTIPLE_CHOICES,82  MOVED_PERMANENTLY,83  MOVED_TEMPORARILY,84  SEE_OTHER,85  NOT_MODIFIED,86  USE_PROXY,87  SWITCH_PROXY,88  TEMPORARY_REDIRECT,89  BAD_REQUEST,90  UNAUTHORIZED,91  PAYMENT_REQUIRED,92  FORBIDDEN,93  NOT_FOUND,94  METHOD_NOT_ALLOWED,95  NOT_ACCEPTABLE,96  PROXY_AUTHENTICATION_REQUIRED,97  REQUEST_TIMEOUT,98  CONFLICT,99  GONE,100  LENGTH_REQUIRED,101  PRECONDITION_FAILED,102  PAYLOAD_TOO_LARGE,103  REQUEST_URI_TOO_LONG,104  UNSUPPORTED_MEDIA_TYPE,105  REQUESTED_RANGE_NOT_SATISFIABLE,106  EXPECTATION_FAILED,107  IM_A_TEAPOT,108  METHOD_FAILURE,109  MISDIRECTED_REQUEST,110  UNPROCESSABLE_ENTITY,111  LOCKED,112  FAILED_DEPENDENCY,113  UPGRADE_REQUIRED,114  PRECONDITION_REQUIRED,115  TOO_MANY_REQUESTS,116  REQUEST_HEADER_FIELDS_TOO_LARGE,117  UNAVAILABLE_FOR_LEGAL_REASONS,118  INTERNAL_SERVER_ERROR,119  NOT_IMPLEMENTED,120  BAD_GATEWAY,121  SERVICE_UNAVAILABLE,122  GATEWAY_TIMEOUT,123  HTTP_VERSION_NOT_SUPPORTED,124  VARIANT_ALSO_NEGOTIATES,125  INSUFFICIENT_STORAGE,126  LOOP_DETECTED,127  NOT_EXTENDED,128  NETWORK_AUTHENTICATION_REQUIRED,129  NETWORK_CONNECT_TIMEOUT_ERROR,130};
```

src/constants/path.constant.js

```
1export const LOGS_ALL = 'logs/all.log';2export const LOGS_ERROR = 'logs/error.log';3
4export default {5  LOGS_ALL,6  LOGS_ERROR,7};
```

and we’ll add to other constants in the future.

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.

Create the .env files

```
1cd ..2
3touch .env .env.example
```

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

.env

```
1NODE_ENV=development2# NODE_ENV=production3PORT=30304DATABASE_URL=mongodb://127.0.0.1:27017/example
```

.env.example

```
1NODE_ENV=development2# NODE_ENV=production3PORT=30304DATABASE_URL=mongodb://
```

after creating the directory structure and `.env` files, we’ll create a file called `index.js`, `bin/www.js`, `config/db.config.js` and `env/variable.env.js` in the `src` directory.

Create the index.js, bin/www.js, db.config.js and variable.env.js files

```
1touch src/index.js src/bin/www.js src/config/db.config.js src/env/variable.env.js
```

new files will be created in the `src` directory, and we’ll add the following code to each of them:

src/config/db.config.js

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

src/env/variable.env.js

```
1import dotenv from 'dotenv';2
3dotenv.config();4
5export const {NODE_ENV} = process.env;6export const {PORT} = process.env;7export const {DATABASE_URL} = process.env;8
9export default {10  NODE_ENV,11  PORT,12  DATABASE_URL,13};
```

src/index.js

```
1import connectDb from './config/db.config';2//http constant3import ConstantHttpCode from './constants/http.code.constant';4import ConstantHttpReason from './constants/http.reason.constant';5import {DATABASE_URL} from './env/variable.env';6import cors from 'cors';7import express from 'express';8
9connectDb(DATABASE_URL);10
11const app = express();12
13app.use(express.urlencoded({extended: true}));14app.use(express.json());15app.use(cors());16
17app.get('/', (req, res, next) => {18  try {19    res.status(ConstantHttpCode.OK).json({20      status: {21        code: ConstantHttpCode.OK,22        msg: ConstantHttpReason.OK,23      },24      API: 'Work',25    });26  } catch (err) {27    next(err);28  }29});30
31export default app;
```

src/bin/www.js

```
1#!/user/bin/env node2import app from '..';3import {PORT} from '../env/variable.env';4import logger from '../utils/logger.util';5import http from 'http';6
7/**8 * Normalize a port into a number, string, or false.9 */10const normalizePort = (val) => {11  const port = parseInt(val, 10);12
13  if (Number.isNaN(port)) {14    // named pipe15    return val;16  }17
18  if (port >= 0) {19    // port number20    return port;21  }22
23  return false;24};25
26const port = normalizePort(PORT || '3000');27app.set('port', port);28
29/**30 * Create HTTP server.31 */32const server = http.createServer(app);33
34/**35 * Event listener for HTTP server "error" event.36 */37const onError = (error) => {38  if (error.syscall !== 'listen') {39    throw error;40  }41
42  const bind = typeof port === 'string' ? `Pipe ${port}` : `Port ${port}`;43
44  // handle specific listen errors with friendly messages45  switch (error.code) {46    case 'EACCES':47      logger.error(`${bind} requires elevated privileges`);48      process.exit(1);49      break;50    case 'EADDRINUSE':51      logger.error(`${bind} is already in use`);52      process.exit(1);53      break;54    default:55      throw error;56  }57};58
59/**60 * Event listener for HTTP server "listening" event.61 */62const onListening = () => {63  const addr = server.address();64  const bind = typeof addr === 'string' ? `pipe ${addr}` : `port ${addr.port}`;65  logger.info(`Listening on ${bind}`);66};67
68server.listen(port);69server.on('error', onError);70server.on('listening', onListening);
```

Now we add a new script to `package.json` so we can run the application:

package.json

```
6 collapsed lines1{2  "name": "tutorial",3  "version": "0.0.0",4  "description": "",5  "keywords": [],6  "main": "index.js",7  "scripts": {8    "start": "node build/bin/www.js",9    "dev": "tsx watch src/bin/www.js",10    "clean": "rm -rf build",11    "build": "pnpm clean && pnpm exec babel src -d build --minified --presets @babel/preset-env",12    "lint": "eslint \"src/**/*.js\" --fix",13    "lint:check": "eslint \"src/**/*.js\"",14    "prettier": "prettier --write \"src/**/*.js\"",15    "prettier:check": "prettier --check \"src/**/*.js\"",16    "prepare": "husky install"17  },49 collapsed lines18  "license": "MIT",19  "author": {20    "name": "YOUR_NAME",21    "email": "YOUR_EMAIL",22    "url": "YOUR_WEBSITE"23  },24  "homepage": "YOUR_GIT_REPO_URL#readme",25  "repository": {26    "type": "git",27    "url": "git+YOUR_GIT_REPO_URL.git"28  },29  "bugs": {30    "url": "YOUR_GIT_REPO_URL/issues"31  },32  "engines": {33    "node": ">=14.0.0",34    "pnpm": ">=8.0.0"35  },36  "dependencies": {37    "@babel/core": "7.18.5",38    "@babel/preset-env": "7.18.2",39    "compression": "1.7.4",40    "cookie-parser": "1.4.6",41    "core-js": "3.23.2",42    "cors": "2.8.5",43    "crypto-js": "4.1.1",44    "dotenv": "16.0.1",45    "express": "4.18.1",46    "helmet": "5.1.0",47    "husky": "8.0.1",48    "jsonwebtoken": "9.0.0",49    "mongoose": "6.11.3",50    "regenerator-runtime": "0.13.9",51    "winston": "3.8.0"52  },53  "devDependencies": {54    "@babel/cli": "7.17.10",55    "@commitlint/cli": "17.0.3",56    "@commitlint/config-conventional": "17.0.3",57    "babel-plugin-module-resolver": "4.1.0",58    "eslint": "8.18.0",59    "eslint-config-airbnb-base": "15.0.0",60    "eslint-config-prettier": "8.5.0",61    "eslint-plugin-import": "2.26.0",62    "eslint-plugin-prettier": "4.0.0",63    "prettier": "2.7.1",64    "tsx": "4.19.2"65  }66}
```

Now you can run the application with `pnpm start` or `pnpm dev`, and you can also run the application with `pnpm build` to create a production version.

Run the application

```
1pnpm dev2
3pnpm start4
5pnpm build
```

Now we’ll add some new packages:

[compression](https://www.npmjs.com/package/compression): Your Node.js app’s main file contains middleware for `compression`. GZIP, which supports a variety of `compression` techniques, will then be enabled. Your JSON response and any static file replies will be smaller as a result.

Install compression

```
1pnpm add compression
```

[cookie-parser](https://www.npmjs.com/package/cookie-parser): Your Node.js app’s main file contains middleware for `cookie-parser`. This middleware will parse the cookies in the request and set them as properties of the request object.

Install cookie-parser

```
1pnpm add cookie-parser
```

[core-js](https://www.npmjs.com/package/core-js): Your Node.js app’s main file contains middleware for `core-js`. This middleware will add the necessary polyfills to your application.

Install core-js

```
1pnpm add core-js
```

[helmet](https://www.npmjs.com/package/helmet): Your Node.js app’s main file contains middleware for `helmet`. This middleware will add security headers to your application.

Install helmet

```
1pnpm add helmet
```

[regenerator-runtime](https://www.npmjs.com/package/regenerator-runtime): Your Node.js app’s main file contains middleware for `regenerator-runtime`. This middleware will add the necessary polyfills to your application.

Install regenerator-runtime

```
1pnpm add regenerator-runtime
```

we’ll change the `index.js` file:

src/index.js

```
1import connectDb from './config/db.config';2//http constant3import ConstantHttpCode from './constants/http.code.constant';4import ConstantHttpReason from './constants/http.reason.constant';5import {DATABASE_URL} from './env/variable.env';6import compression from 'compression';7import cookieParser from 'cookie-parser';8import cors from 'cors';9import express from 'express';10import helmet from 'helmet';11
12connectDb(DATABASE_URL);13
14const app = express();15
16//helmet17app.use(helmet());18
19app.use(express.urlencoded({extended: true}));20app.use(express.json());21app.use(compression());22app.use(cors());23app.use(cookieParser());24
25app.get('/', (req, res, next) => {26  try {27    res.status(ConstantHttpCode.OK).json({28      status: {29        code: ConstantHttpCode.OK,30        msg: ConstantHttpReason.OK,31      },32      API: 'Work',33    });34  } catch (err) {35    next(err);36  }37});38
39export default app;
```

and we’ll change the `bin/www.js` file:

src/bin/www.js

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

Now we’ll run the application with `pnpm dev`:

Run the application

```
1╭─mkabumattar@mkabumattar in ~/work/tutorial is  v0.0.0 via  v18.3.0 took 243ms2╰─λ pnpm dev3
4> tutorial@0.0.0 dev5> tsx watch src/bin/www.js6
72022-06-25 11:57:38:5738 info: Listening on port 303082022-06-25 11:57:38:5738 info: Mongo DB is connected to: 127.0.0.1
```

## [Git hooks](#git-hooks)

Before moving on to component development, there is one more section on configuration. If you want to expand on this project in the future, especially with a team of other developers, keep in mind that you’ll want it to be as stable as possible. To get it right from the beginning is time well spent.

We’re going to use a program called [Husky](https://husky.run/).

### [Husky](#husky)

Husky is a tool for executing scripts at various git stages, such as add, commit, push, etc. We would like to be able to specify requirements and, provided our project is of acceptable quality, only enable actions like commit and push to proceed if our code satisfies those requirements.

To install Husky run

Install Husky

```
1pnpm add husky2
3git init4
5pnpm exec husky install
```

This will create a `.husky` directory in your project. Your hooks will be located here. As it is meant for other developers as well as yourself, make sure this directory is included in your code repository.

Create the .gitignore file

```
1touch .gitignore
```

.gitignore

```
1# Logs2logs3*.log4npm-debug.log*5yarn-debug.log*6yarn-error.log*7lerna-debug.log*8.pnpm-debug.log*9
10# Diagnostic reports (https://nodejs.org/api/report.html)11report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json12
13# Runtime data14pids15*.pid16*.seed17*.pid.lock18
19# Directory for instrumented libs generated by jscoverage/JSCover20lib-cov21
22# Coverage directory used by tools like istanbul23coverage24*.lcov25
26# nyc test coverage27.nyc_output28
29# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)30.grunt31
32# Bower dependency directory (https://bower.io/)33bower_components34
35# node-waf configuration36.lock-wscript37
38# Compiled binary addons (https://nodejs.org/api/addons.html)39build40build/Release41
42# Dependency directories43node_modules/44jspm_packages/45
46# Snowpack dependency directory (https://snowpack.dev/)47web_modules/48
49# TypeScript cache50*.tsbuildinfo51
52# Optional npm cache directory53.npm54
55# Optional eslint cache56.eslintcache57
58# Optional stylelint cache59.stylelintcache60
61# Microbundle cache62.rpt2_cache/63.rts2_cache_cjs/64.rts2_cache_es/65.rts2_cache_umd/66
67# Optional REPL history68.node_repl_history69
70# Output of 'npm pack'71*.tgz72
73# Yarn Integrity file74.yarn-integrity75
76# dotenv environment variable files77.env78.env.development.local79.env.test.local80.env.production.local81.env.local82
83# parcel-bundler cache (https://parceljs.org/)84.cache85.parcel-cache86
87# vuepress build output88.vuepress/dist89
90# vuepress v2.x temp and cache directory91.temp92.cache93
94# Docusaurus cache and generated files95.docusaurus96
97# Serverless directories98.serverless/99
100# FuseBox cache101.fusebox/102
103# DynamoDB Local files104.dynamodb/105
106# TernJS port file107.tern-port108
109# Stores VSCode versions used for testing VSCode extensions110.vscode-test111
112# yarn v2113.yarn/cache114.yarn/unplugged115.yarn/build-state.yml116.yarn/install-state.gz117.pnp.*
```

Notice that this `.gitignore` excludes `build` and `.env` but not `.husky`. As noted above, the hooks directory is meant for the other developers too, so it stays in the repository.

Add the following script to your `package.json` file:

package.json

```
6 collapsed lines1{2  "name": "tutorial",3  "version": "0.0.0",4  "description": "",5  "keywords": [],6  "main": "index.js",7  "scripts": {8 collapsed lines8    "start": "node build/bin/www.js",9    "dev": "tsx watch src/bin/www.js",10    "clean": "rm -rf build",11    "build": "pnpm clean && pnpm exec babel src -d build --minified --presets @babel/preset-env",12    "lint": "eslint \"src/**/*.js\" --fix",13    "lint:check": "eslint \"src/**/*.js\"",14    "prettier": "prettier --write \"src/**/*.js\"",15    "prettier:check": "prettier --check \"src/**/*.js\"",16    "prepare": "husky install"17  },49 collapsed lines18  "license": "MIT",19  "author": {20    "name": "YOUR_NAME",21    "email": "YOUR_EMAIL",22    "url": "YOUR_WEBSITE"23  },24  "homepage": "YOUR_GIT_REPO_URL#readme",25  "repository": {26    "type": "git",27    "url": "git+YOUR_GIT_REPO_URL.git"28  },29  "bugs": {30    "url": "YOUR_GIT_REPO_URL/issues"31  },32  "engines": {33    "node": ">=14.0.0",34    "pnpm": ">=8.0.0"35  },36  "dependencies": {37    "@babel/core": "7.18.5",38    "@babel/preset-env": "7.18.2",39    "compression": "1.7.4",40    "cookie-parser": "1.4.6",41    "core-js": "3.23.2",42    "cors": "2.8.5",43    "crypto-js": "4.1.1",44    "dotenv": "16.0.1",45    "express": "4.18.1",46    "helmet": "5.1.0",47    "husky": "8.0.1",48    "jsonwebtoken": "9.0.0",49    "mongoose": "6.11.3",50    "regenerator-runtime": "0.13.9",51    "winston": "3.8.0"52  },53  "devDependencies": {54    "@babel/cli": "7.17.10",55    "@commitlint/cli": "17.0.3",56    "@commitlint/config-conventional": "17.0.3",57    "babel-plugin-module-resolver": "4.1.0",58    "eslint": "8.18.0",59    "eslint-config-airbnb-base": "15.0.0",60    "eslint-config-prettier": "8.5.0",61    "eslint-plugin-import": "2.26.0",62    "eslint-plugin-prettier": "4.0.0",63    "prettier": "2.7.1",64    "tsx": "4.19.2"65  }66}
```

To create a hook run:

Create a hook

```
1pnpm exec husky add .husky/pre-commit "pnpm lint"
```

The aforementioned states that the `pnpm lint` script must run and be successful before our commit may be successful. Success here refers to the absence of mistakes. You will be able to get warnings (remember in the ESLint config a setting of 1 is a warning and 2 is an error in case you want to adjust settings).

We’re going to add another one:

Create a hook

```
1pnpm exec husky add .husky/pre-push "pnpm build"
```

This makes sure that we can’t push to the remote repository until our code has built correctly. That sounds like a very acceptable requirement, don’t you think? Test it by making this adjustment and then attempting to push.

### [Commitlint](#commitlint)

Finally, we’ll add one more tool. We have been using a uniform format for all of our commit messages so far, so let’s make sure that everyone on the team is adhering to it as well (including ourselves!). For our commit messages, we may add a linter.

Install Commitlint

```
1pnpm add -D @commitlint/config-conventional @commitlint/cli
```

We will configure it using a set of common defaults, but since I occasionally forget what prefixes are available, I like to explicitly provide that list in a `commitlint.config.js` file:

Create a commitlint.config.js file

```
1touch commitlint.config.js
```

commitlint.config.js

```
1// build: Changes that affect the build system or external dependencies (example scopes: gulp, broccoli, npm)2// ci: Changes to our CI configuration files and scripts (example scopes: Travis, Circle, BrowserStack, SauceLabs)3// docs: Documentation only changes4// feat: A new feature5// fix: A bug fix6// perf: A code change that improves performance7// refactor: A code change that neither fixes a bug nor adds a feature8// style: Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)9// test: Adding missing tests or correcting existing tests10
11module.exports = {12  extends: ['@commitlint/config-conventional'],13  rules: {14    'body-leading-blank': [1, 'always'],15    'body-max-line-length': [2, 'always', 100],16    'footer-leading-blank': [1, 'always'],17    'footer-max-line-length': [2, 'always', 100],18    'header-max-length': [2, 'always', 100],19    'scope-case': [2, 'always', 'lower-case'],20    'subject-case': [21      2,22      'never',23      ['sentence-case', 'start-case', 'pascal-case', 'upper-case'],24    ],25    'subject-empty': [2, 'never'],26    'subject-full-stop': [2, 'never', '.'],27    'type-case': [2, 'always', 'lower-case'],28    'type-empty': [2, 'never'],29    'type-enum': [30      2,31      'always',32      [33        'build',34        'chore',35        'ci',36        'docs',37        'feat',38        'fix',39        'perf',40        'refactor',41        'revert',42        'style',43        'test',44        'translation',45        'security',46        'changeset',47      ],48    ],49  },50};
```

Afterward, use Husky to enable commitlint by using:

Create a hook

```
1pnpm exec husky add .husky/commit-msg 'pnpm exec commitlint --edit "$1"'
```

Now connect this repository to GitHub and push your commits. This uses an SSH remote (`git@github.com:`), so set up your [SSH keys](/blog/post/git-ssh-keys-for-github-gitlab-and-bitbucket-on-linux) first if you haven’t already.

Push to GitHub

```
1echo "# Setting up Node JS, Express, MongoDB, Prettier, ESLint and Husky Application with Babel and authentication as an example" >> README.md2git init3git add README.md4git commit -m "ci: Initial commit"5git branch -M main6git remote add origin git@github.com:<your-github-username>/<your-github-repository-name>.git7git push -u origin main
```

## [VS Code](#vs-code)

### [Configuration](#configuration)

We can now take advantage of some useful VS Code functionality to have ESLint and Prettier run automatically since we have implemented them.

Make a `settings.json` file and a directory called `.vscode` at the top of your project. This will be a list of values that overrides the VS Code installation’s default settings.

Because we may set up particular parameters that only apply to this project and share them with the rest of our team by adding them to the code repository, we want to put them in a folder for the project.

Within `settings.json` we will add the following values:

Create the settings.json file

```
1mkdir .vscode2touch .vscode/settings.json
```

`settings.json`

"./

```
1{2  "editor.defaultFormatter": "esbenp.prettier-vscode",3  "editor.formatOnSave": true,4  "editor.codeActionsOnSave": {5    "source.fixAll": true,6    "source.organizeImports": true7  }8}
```

### [Debugging](#debugging)

In case we encounter any problems while developing our program, let’s set up a handy environment for debugging.

Inside of your `.vscode` directory create a `launch.json` file:

Terminal window

```
1touch .vscode/launch.json
```

.vscode/launch.json

```
1{2  "version": "0.1.0",3  "configurations": [4    {5      "name": "debug server",6      "type": "node-terminal",7      "request": "launch",8      "command": "pnpm dev"9    }10  ]11}
```

## [Authentication](#authentication)

### [Authentication setup](#authentication-setup)

We’ll add a new packages to our project:

-   [crypto-js](https://www.npmjs.com/package/crypto-js): A JavaScript library for encryption and decryption.
-   [jsonwebtoken](https://www.npmjs.com/package/jsonwebtoken): A JavaScript library for creating and verifying JSON Web Tokens.

Install crypto-js and jsonwebtoken

```
1pnpm add crypto-js jsonwebtoken
```

add secret key to `.env` file:

.env

```
1...2JWT_SECRET=secret3PASS_SECRET=secret
```

add `JWT_SECRET` and `PASS_SECRET` to `variable.env.js` file:

src/env/variable.env.js

```
1...2
3export const { JWT_SECRET } = process.env4export const { PASS_SECRET } = process.env5
6export default {7  ...,8  JWT_SECRET,9  PASS_SECRET,10}
```

now we’ll add the constants to:

src/constants/api.constant.js

```
1// api2export const API_AUTH = '/api/auth';3export const API_USERS = '/api/users';4
5// auth6export const AUTH_REGISTER = '/register';7export const AUTH_LOGIN = '/login';8
9// users10export const USER_UPDATE_USERNAME = '/update-username/:id';11export const USER_UPDATE_NAME = '/update-name/:id';12export const USER_UPDATE_EMAIL = '/update-email/:id';13export const USER_UPDATE_PASSWORD = '/update-password/:id';14export const USER_UPDATE_PHONE = '/update-phone/:id';15export const USER_UPDATE_ADDRESS = '/update-address/:id';16export const USER_DELETE = '/delete/:id';17export const USER_GET = '/find/:id';18export const USER_GET_ALL = '/';19export const USER_GET_ALL_STATS = '/stats';20
21export default {22  // api23  API_AUTH,24  API_USERS,25
26  // auth27  AUTH_REGISTER,28  AUTH_LOGIN,29
30  // users31  USER_UPDATE_USERNAME,32  USER_UPDATE_NAME,33  USER_UPDATE_EMAIL,34  USER_UPDATE_PASSWORD,35  USER_UPDATE_PHONE,36  USER_UPDATE_ADDRESS,37  USER_DELETE,38  USER_GET,39  USER_GET_ALL,40  USER_GET_ALL_STATS,41};
```

src/constants/message.constant.js

```
1// token2export const TOKEN_NOT_VALID = 'Token not valid';3export const NOT_AUTHENTICATED = 'Not authenticated';4export const NOT_ALLOWED = 'Not allowed';5
6// auth7export const USERNAME_NOT_VALID = 'username is not valid';8export const NAME_NOT_VALID = 'name is not valid';9export const EMAIL_NOT_VALID = 'email is not valid';10export const PASSWORD_NOT_VALID = 'password is not valid';11export const PHONE_NOT_VALID = 'phone is not valid';12export const ADDRESS_NOT_VALID = 'address is not valid';13export const USERNAME_EXIST = 'username is exist';14export const EMAIL_EXIST = 'email is exist';15export const PHONE_EXIST = 'phone is exist';16export const USER_NOT_CREATE = 'user is not create, please try again';17export const USER_CREATE_SUCCESS = 'user is create success, please login';18export const USER_NOT_FOUND = 'user is not found';19export const PASSWORD_NOT_MATCH = 'password is not match';20export const USER_LOGIN_SUCCESS = 'user is login success';21
22// user23export const USERNAME_NOT_CHANGE = 'username is not change';24export const USERNAME_CHANGE_SUCCESS = 'username is change success';25export const NAME_NOT_CHANGE = 'name is not change';26export const NAME_CHANGE_SUCCESS = 'name is change success';27export const EMAIL_NOT_CHANGE = 'email is not change';28export const EMAIL_CHANGE_SUCCESS = 'email is change success';29export const PASSWORD_NOT_CHANGE = 'password is not change';30export const PASSWORD_CHANGE_SUCCESS = 'password is change success';31export const PHONE_NOT_CHANGE = 'phone is not change';32export const PHONE_CHANGE_SUCCESS = 'phone is change success';33export const ADDRESS_NOT_CHANGE = 'address is not change';34export const ADDRESS_CHANGE_SUCCESS = 'address is change success';35export const USER_NOT_DELETE = 'user is not delete, please try again';36export const USER_DELETE_SUCCESS = 'user is delete success';37export const USER_FOUND = 'user is found';38
39export default {40  // token41  TOKEN_NOT_VALID,42  NOT_AUTHENTICATED,43  NOT_ALLOWED,44
45  // auth46  USERNAME_NOT_VALID,47  NAME_NOT_VALID,48  EMAIL_NOT_VALID,49  PASSWORD_NOT_VALID,50  PHONE_NOT_VALID,51  ADDRESS_NOT_VALID,52  USERNAME_EXIST,53  EMAIL_EXIST,54  PHONE_EXIST,55  USER_NOT_CREATE,56  USER_CREATE_SUCCESS,57  USER_NOT_FOUND,58  PASSWORD_NOT_MATCH,59  USER_LOGIN_SUCCESS,60
61  // user62  USERNAME_NOT_CHANGE,63  USERNAME_CHANGE_SUCCESS,64  NAME_NOT_CHANGE,65  NAME_CHANGE_SUCCESS,66  EMAIL_NOT_CHANGE,67  EMAIL_CHANGE_SUCCESS,68  PASSWORD_NOT_CHANGE,69  PASSWORD_CHANGE_SUCCESS,70  PHONE_NOT_CHANGE,71  PHONE_CHANGE_SUCCESS,72  ADDRESS_NOT_CHANGE,73  ADDRESS_CHANGE_SUCCESS,74  USER_NOT_DELETE,75  USER_DELETE_SUCCESS,76  USER_FOUND,77};
```

src/constants/model.constant.js

```
1export const USER_MODEL = 'UserModel';2
3export default {4  USER_MODEL,5};
```

src/constants/number.constant.js

```
1// user2export const USERNAME_MIN_LENGTH = 3;3export const USERNAME_MAX_LENGTH = 20;4export const NAME_MIN_LENGTH = 3;5export const NAME_MAX_LENGTH = 80;6export const EMAIL_MAX_LENGTH = 50;7export const PASSWORD_MIN_LENGTH = 8;8export const PHONE_MIN_LENGTH = 10;9export const PHONE_MAX_LENGTH = 20;10export const ADDRESS_MIN_LENGTH = 10;11export const ADDRESS_MAX_LENGTH = 200;12
13export default {14  USERNAME_MIN_LENGTH,15  USERNAME_MAX_LENGTH,16  NAME_MIN_LENGTH,17  NAME_MAX_LENGTH,18  EMAIL_MAX_LENGTH,19  PASSWORD_MIN_LENGTH,20  PHONE_MIN_LENGTH,21  PHONE_MAX_LENGTH,22  ADDRESS_MIN_LENGTH,23  ADDRESS_MAX_LENGTH,24};
```

src/constants/regex.constant.js

```
1export const USERNAME = /^(?!.*\.\.)(?!.*\.$)[^\W][\w.]{3,32}$/;2export const EMAIL =3  /^(([^<>()\\[\]\\.,;:\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,}))$/;4export const PASSWORD =5  /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/;6export const NAME = /^[a-zA-Z ]{2,35}$/;7export const PHONE =8  /^\s*(?:\+?(\d{1,3}))?([-. (]*(\d{3})[-. )]*)?((\d{3})[-. ]*(\d{2,4})(?:[-.x ]*(\d+))?)\s*$/gm;9export const ADDRESS = /^[a-zA-Z0-9\s,'-]{10,200}$/;10
11export default {12  USERNAME,13  EMAIL,14  PASSWORD,15  NAME,16  PHONE,17  ADDRESS,18};
```

### [Authentication middleware](#authentication-middleware)

src/middlewares/token.middleware.js

```
1// http constant2import ConstantHttpCode from '../constants/http.code.constant';3import ConstantHttpReason from '../constants/http.reason.constant';4import ConstantMessage from '../constants/message.constant';5import {JWT_SECRET} from '../env/variable.env';6// logger7import logger from '../utils/logger.util';8import jwt from 'jsonwebtoken';9
10export const verifyToken = (req, res, next) => {11  const authHeader = req.headers.token;12  logger.info(`authHeader: ${authHeader}`);13  if (authHeader) {14    const token = authHeader.split(' ')[1];15    return jwt.verify(token, JWT_SECRET, (err, user) => {16      if (err) {17        res.status(ConstantHttpCode.FORBIDDEN).json({18          status: {19            code: ConstantHttpCode.FORBIDDEN,20            msg: ConstantHttpReason.FORBIDDEN,21          },22          msg: ConstantMessage.TOKEN_NOT_VALID,23        });24      }25      req.user = user;26      return next();27    });28  }29
30  return res.status(ConstantHttpCode.UNAUTHORIZED).json({31    status: {32      code: ConstantHttpCode.UNAUTHORIZED,33      msg: ConstantHttpReason.UNAUTHORIZED,34    },35    msg: ConstantMessage.NOT_AUTHENTICATED,36  });37};38
39export const verifyTokenAndAuthorization = (req, res, next) => {40  verifyToken(req, res, () => {41    if (req.user.id === req.params.id || req.user.isAdmin) {42      return next();43    }44
45    return res.status(ConstantHttpCode.FORBIDDEN).json({46      status: {47        code: ConstantHttpCode.FORBIDDEN,48        msg: ConstantHttpReason.FORBIDDEN,49      },50      msg: ConstantMessage.NOT_ALLOWED,51    });52  });53};54
55export const verifyTokenAndAdmin = (req, res, next) => {56  verifyToken(req, res, () => {57    if (req.user.isAdmin) {58      return next();59    }60
61    return res.status(ConstantHttpCode.FORBIDDEN).json({62      status: {63        code: ConstantHttpCode.FORBIDDEN,64        msg: ConstantHttpReason.FORBIDDEN,65      },66      msg: ConstantMessage.NOT_ALLOWED,67    });68  });69};70
71export default {72  verifyToken,73  verifyTokenAndAuthorization,74  verifyTokenAndAdmin,75};
```

### [Authentication security](#authentication-security)

src/security/user.security.js

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

### [Authentication validations](#authentication-validations)

src/validations/user.validation.js

```
1import ConstantRegex from '../constants/regex.constant';2
3export const validateUsername = async (username) => {4  return ConstantRegex.USERNAME.test(username);5};6
7export const validateName = async (name) => {8  return ConstantRegex.NAME.test(name);9};10
11export const validateEmail = async (email) => {12  return ConstantRegex.EMAIL.test(email);13};14
15export const validatePassword = async (password) => {16  return ConstantRegex.PASSWORD.test(password);17};18
19export const validatePhone = async (phone) => {20  return ConstantRegex.PHONE.test(phone);21};22
23export const validateAddress = async (address) => {24  return ConstantRegex.ADDRESS.test(address);25};26
27export default {28  validateUsername,29  validateName,30  validateEmail,31  validatePassword,32  validatePhone,33  validateAddress,34};
```

### [Authentication schemas](#authentication-schemas)

src/schemas/user.schema.js

```
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;
```

### [Authentication models](#authentication-models)

src/models/user.model.js

```
1import ConstantModel from '../constants/model.constant';2import UserSchema from '../schemas/user.schema';3import mongoose from 'mongoose';4
5const UserModel = mongoose.model(ConstantModel.USER_MODEL, UserSchema);6
7export default UserModel;
```

### [Authentication repositories](#authentication-repositories)

src/repositories/user.repository.js

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

### [Authentication services](#authentication-services)

src/services/auth.service.js

```
1import UserRepository from '../repositories/user.repository';2import UserSecurity from '../security/user.security';3import UserValidation from '../validations/user.validation';4
5export const validateUsername = (username) => {6  return UserValidation.validateUsername(username);7};8
9export const validateName = (name) => {10  return UserValidation.validateName(name);11};12
13export const validateEmail = (email) => {14  return UserValidation.validateEmail(email);15};16
17export const validatePassword = (password) => {18  return UserValidation.validatePassword(password);19};20
21export const comparePassword = (password, encryptedPassword) => {22  return UserSecurity.comparePassword(password, encryptedPassword);23};24
25export const validatePhone = (phone) => {26  return UserValidation.validatePhone(phone);27};28
29export const validateAddress = (address) => {30  return UserValidation.validateAddress(address);31};32
33export const findByUser = async (username) => {34  const user = await UserRepository.findByUser(username);35  return user;36};37
38export const findByEmail = async (email) => {39  const user = await UserRepository.findByEmailWithPassword(email);40  return user;41};42
43export const findByPhone = async (phone) => {44  const user = await UserRepository.findByPhone(phone);45  return user;46};47
48export const createUser = async (user) => {49  const encryptedPassword = UserSecurity.encryptedPassword(user.password);50  const newUser = {51    username: user.username,52    name: user.name,53    email: user.email,54    password: encryptedPassword,55    phone: user.phone,56    address: user.address,57    isAdmin: user.isAdmin,58  };59  const savedUser = await UserRepository.createUser(newUser);60  return savedUser;61};62
63export const generateAccessToken = async (user) => {64  return `Bearer ${UserSecurity.generateAccessToken(user.id, user.isAdmin)}`;65};66
67export default {68  validateUsername,69  validateName,70  validateEmail,71  validatePassword,72  comparePassword,73  validatePhone,74  validateAddress,75  findByUser,76  findByEmail,77  findByPhone,78  createUser,79  generateAccessToken,80};
```

src/services/user.service.js

```
1import UserRepository from '../repositories/user.repository';2import UserSecurity from '../security/user.security';3import UserValidation from '../validations/user.validation';4
5export const validateUsername = (username) => {6  return UserValidation.validateUsername(username);7};8
9export const validateName = (name) => {10  return UserValidation.validateName(name);11};12
13export const validateEmail = (email) => {14  return UserValidation.validateEmail(email);15};16
17export const validatePassword = (name) => {18  return UserValidation.validatePassword(name);19};20
21export const comparePassword = (password, encryptedPassword) => {22  return UserSecurity.comparePassword(password, encryptedPassword);23};24
25export const validatePhone = (phone) => {26  return UserValidation.validatePhone(phone);27};28
29export const validateAddress = (address) => {30  return UserValidation.validateAddress(address);31};32
33export const findAll = async () => {34  const users = await UserRepository.findAll();35  return users;36};37
38export const findById = async (id) => {39  const user = await UserRepository.findByIdWithPassword(id);40  return user;41};42
43export const findByIdWithOutPassword = async (id) => {44  const user = await UserRepository.findById(id);45  return user;46};47
48export const findByEmail = async (email) => {49  const user = await UserRepository.findByEmail(email);50  return user;51};52
53export const findByPhone = async (phone) => {54  const user = await UserRepository.findByPhone(phone);55  return user;56};57
58export const findByUser = async (username) => {59  const user = await UserRepository.findByUser(username);60  return user;61};62
63export const updateUsername = async (id, username) => {64  const user = await UserRepository.updateUsername(id, username);65  return user;66};67
68export const updateName = async (id, name) => {69  const user = await UserRepository.updateName(id, name);70  return user;71};72
73export const updateEmail = async (id, email) => {74  const user = await UserRepository.updateEmail(id, email);75  return user;76};77
78export const updatePassword = async (id, password) => {79  const encryptedPassword = UserSecurity.encryptedPassword(password);80  const user = await UserRepository.updatePassword(id, encryptedPassword);81  return user;82};83
84export const updatePhone = async (id, phone) => {85  const user = await UserRepository.updatePhone(id, phone);86  return user;87};88
89export const updateAddress = async (id, address) => {90  const user = await UserRepository.updateAddress(id, address);91  return user;92};93
94export const deleteUser = async (id) => {95  const user = await UserRepository.deleteUser(id);96  return user;97};98
99export const getUsersStats = async () => {100  const users = await UserRepository.getUsersStats();101  return users;102};103
104export default {105  validateUsername,106  validateName,107  validateEmail,108  validatePassword,109  comparePassword,110  validatePhone,111  validateAddress,112  findAll,113  findById,114  findByIdWithOutPassword,115  findByEmail,116  findByPhone,117  findByUser,118  updateUsername,119  updateName,120  updateEmail,121  updatePassword,122  updatePhone,123  updateAddress,124  deleteUser,125  getUsersStats,126};
```

### [Authentication controllers](#authentication-controllers)

`auth.controller.js`

```
1// http constant2import ConstantHttpCode from '../constants/http.code.constant';3import ConstantHttpReason from '../constants/http.reason.constant';4import ConstantMessage from '../constants/message.constant';5import AuthServices from '../services/auth.service';6// logger7import logger from '../utils/logger.util';8
9export const register = async (req, res, next) => {10  try {11    const {username, name, email, password, phone, address} = req.body;12
13    const usernameValidated = AuthServices.validateUsername(username);14    if (!usernameValidated) {15      return res.status(ConstantHttpCode.BAD_REQUEST).json({16        status: {17          code: ConstantHttpCode.BAD_REQUEST,18          msg: ConstantHttpReason.BAD_REQUEST,19        },20        msg: ConstantMessage.USERNAME_NOT_VALID,21      });22    }23    logger.info(`username ${username} is valid`);24
25    const nameValidated = AuthServices.validateName(name);26    if (!nameValidated) {27      return res.status(ConstantHttpCode.BAD_REQUEST).json({28        status: {29          code: ConstantHttpCode.BAD_REQUEST,30          msg: ConstantHttpReason.BAD_REQUEST,31        },32        msg: ConstantMessage.NAME_NOT_VALID,33      });34    }35    logger.info(`name ${name} is valid`);36
37    const emailValidated = AuthServices.validateEmail(email);38    if (!emailValidated) {39      return res.status(ConstantHttpCode.BAD_REQUEST).json({40        status: {41          code: ConstantHttpCode.BAD_REQUEST,42          msg: ConstantHttpReason.BAD_REQUEST,43        },44        msg: ConstantMessage.EMAIL_NOT_VALID,45      });46    }47    logger.info(`email ${email} is valid`);48
49    const passwordValidated = AuthServices.validatePassword(password);50    if (!passwordValidated) {51      return res.status(ConstantHttpCode.BAD_REQUEST).json({52        status: {53          code: ConstantHttpCode.BAD_REQUEST,54          msg: ConstantHttpReason.BAD_REQUEST,55        },56        msg: ConstantMessage.PASSWORD_NOT_VALID,57      });58    }59
60    const phoneValidated = AuthServices.validatePhone(phone);61    if (!phoneValidated) {62      return res.status(ConstantHttpCode.BAD_REQUEST).json({63        status: {64          code: ConstantHttpCode.BAD_REQUEST,65          msg: ConstantHttpReason.BAD_REQUEST,66        },67        msg: ConstantMessage.PHONE_NOT_VALID,68      });69    }70
71    const addressValidated = AuthServices.validateAddress(address);72    if (!addressValidated) {73      return res.status(ConstantHttpCode.BAD_REQUEST).json({74        status: {75          code: ConstantHttpCode.BAD_REQUEST,76          msg: ConstantHttpReason.BAD_REQUEST,77        },78        msg: ConstantMessage.ADDRESS_NOT_VALID,79      });80    }81
82    const usernameCheck = await AuthServices.findByUser(username);83    if (usernameCheck) {84      return res.status(ConstantHttpCode.BAD_REQUEST).json({85        status: {86          code: ConstantHttpCode.BAD_REQUEST,87          msg: ConstantHttpReason.BAD_REQUEST,88        },89        msg: ConstantMessage.USERNAME_EXIST,90      });91    }92
93    const emailCheck = await AuthServices.findByEmail(email);94    if (emailCheck) {95      return res.status(ConstantHttpCode.BAD_REQUEST).json({96        status: {97          code: ConstantHttpCode.BAD_REQUEST,98          msg: ConstantHttpReason.BAD_REQUEST,99        },100        msg: ConstantMessage.EMAIL_EXIST,101      });102    }103
104    const phoneCheck = await AuthServices.findByPhone(phone);105    if (phoneCheck) {106      return res.status(ConstantHttpCode.BAD_REQUEST).json({107        status: {108          code: ConstantHttpCode.BAD_REQUEST,109          msg: ConstantHttpReason.BAD_REQUEST,110        },111        msg: ConstantMessage.PHONE_EXIST,112      });113    }114
115    const newUserData = {116      username,117      name,118      email,119      password,120      phone,121      address,122    };123
124    const user = await AuthServices.createUser(newUserData);125    if (!user) {126      return res.status(ConstantHttpCode.BAD_REQUEST).json({127        status: {128          code: ConstantHttpCode.BAD_REQUEST,129          msg: ConstantHttpReason.BAD_REQUEST,130        },131        msg: ConstantMessage.USER_NOT_CREATE,132      });133    }134
135    const newUser = {...user}._doc;136
137    logger.info({newUserpassword: newUser.password});138
139    delete newUser.password;140
141    logger.info({newUserpassword: newUser.password});142
143    return res.status(ConstantHttpCode.CREATED).json({144      status: {145        code: ConstantHttpCode.CREATED,146        msg: ConstantHttpReason.CREATED,147      },148      msg: ConstantMessage.USER_CREATE_SUCCESS,149      data: user,150    });151  } catch (err) {152    return next(err);153  }154};155
156export const login = async (req, res, next) => {157  try {158    const {email, password} = req.body;159
160    const emailValidated = AuthServices.validateEmail(email);161    if (!emailValidated) {162      return res.status(ConstantHttpCode.BAD_REQUEST).json({163        status: {164          code: ConstantHttpCode.BAD_REQUEST,165          msg: ConstantHttpReason.BAD_REQUEST,166        },167        msg: ConstantMessage.EMAIL_NOT_VALID,168      });169    }170
171    const passwordValidated = AuthServices.validatePassword(password);172    if (!passwordValidated) {173      return res.status(ConstantHttpCode.BAD_REQUEST).json({174        status: {175          code: ConstantHttpCode.BAD_REQUEST,176          msg: ConstantHttpReason.BAD_REQUEST,177        },178        msg: ConstantMessage.PASSWORD_NOT_VALID,179      });180    }181
182    const user = await AuthServices.findByEmail(email);183    if (!user) {184      return res.status(ConstantHttpCode.BAD_REQUEST).json({185        status: {186          code: ConstantHttpCode.BAD_REQUEST,187          msg: ConstantHttpReason.BAD_REQUEST,188        },189        msg: ConstantMessage.USER_NOT_FOUND,190      });191    }192
193    const isMatch = AuthServices.comparePassword(password, user.password);194    if (!isMatch) {195      return res.status(ConstantHttpCode.BAD_REQUEST).json({196        status: {197          code: ConstantHttpCode.BAD_REQUEST,198          msg: ConstantHttpReason.BAD_REQUEST,199        },200        msg: ConstantMessage.PASSWORD_NOT_MATCH,201      });202    }203
204    const accessToken = await AuthServices.generateAccessToken(user);205    logger.info(`accessToken: ${accessToken}`);206
207    const newUser = {...user}._doc;208
209    logger.info({newUserpassword: newUser.password});210
211    delete newUser.password;212
213    logger.info({newUserpassword: newUser.password});214
215    return res.status(ConstantHttpCode.OK).json({216      status: {217        code: ConstantHttpCode.OK,218        msg: ConstantHttpReason.OK,219      },220      msg: ConstantMessage.LOGIN_SUCCESS,221      data: {222        user,223        accessToken,224      },225    });226  } catch (err) {227    return next(err);228  }229};230
231export default {232  register,233  login,234};
```

`user.controller.js`

```
1// http constant2import ConstantHttpCode from '../constants/http.code.constant';3import ConstantHttpReason from '../constants/http.reason.constant';4import ConstantMessage from '../constants/message.constant';5import UserService from '../services/user.service';6// logger7import logger from '../utils/logger.util';8
9export const updateUsername = async (req, res, next) => {10  try {11    const {username, password} = req.body;12    const {id} = req.params;13
14    const user = await UserService.findById(id);15    if (!user) {16      return res.status(ConstantHttpCode.NOT_FOUND).json({17        status: {18          code: ConstantHttpCode.NOT_FOUND,19          msg: ConstantHttpReason.NOT_FOUND,20        },21        msg: ConstantMessage.USER_NOT_FOUND,22      });23    }24    logger.info(`user ${user.username} found`);25
26    const usernameValidated = UserService.validateUsername(username);27    if (!usernameValidated) {28      return res.status(ConstantHttpCode.BAD_REQUEST).json({29        status: {30          code: ConstantHttpCode.BAD_REQUEST,31          msg: ConstantHttpReason.BAD_REQUEST,32        },33        msg: ConstantMessage.USERNAME_NOT_VALID,34      });35    }36    logger.info(`username ${username} is valid`);37
38    const passwordValidated = UserService.validatePassword(password);39    if (!passwordValidated) {40      return res.status(ConstantHttpCode.BAD_REQUEST).json({41        status: {42          code: ConstantHttpCode.BAD_REQUEST,43          msg: ConstantHttpReason.BAD_REQUEST,44        },45        msg: ConstantMessage.PASSWORD_NOT_VALID,46      });47    }48    logger.info(`password ${password} is valid`);49
50    const isMatch = UserService.comparePassword(password, user.password);51    if (!isMatch) {52      return res.status(ConstantHttpCode.BAD_REQUEST).json({53        status: {54          code: ConstantHttpCode.BAD_REQUEST,55          msg: ConstantHttpReason.BAD_REQUEST,56        },57        msg: ConstantMessage.PASSWORD_NOT_MATCH,58      });59    }60
61    const usernameCheck = await UserService.findByUser(username);62    if (usernameCheck) {63      return res.status(ConstantHttpCode.BAD_REQUEST).json({64        status: {65          code: ConstantHttpCode.BAD_REQUEST,66          msg: ConstantHttpReason.BAD_REQUEST,67        },68        msg: ConstantMessage.USERNAME_EXIST,69      });70    }71
72    if (user.username === username) {73      return res.status(ConstantHttpCode.BAD_REQUEST).json({74        status: {75          code: ConstantHttpCode.BAD_REQUEST,76          msg: ConstantHttpReason.BAD_REQUEST,77        },78        msg: ConstantMessage.USERNAME_NOT_CHANGE,79      });80    }81
82    const updatedUser = await UserService.updateUsername(id, username);83    if (!updatedUser) {84      return res.status(ConstantHttpCode.BAD_REQUEST).json({85        status: {86          code: ConstantHttpCode.BAD_REQUEST,87          msg: ConstantHttpReason.BAD_REQUEST,88        },89        msg: ConstantMessage.USERNAME_NOT_CHANGE,90      });91    }92    logger.info(`user ${user.username} updated`);93
94    return res.status(ConstantHttpCode.OK).json({95      status: {96        code: ConstantHttpCode.OK,97        msg: ConstantHttpReason.OK,98      },99      msg: ConstantMessage.USERNAME_CHANGE_SUCCESS,100      data: {101        user: updatedUser,102      },103    });104  } catch (err) {105    return next(err);106  }107};108
109export const updateName = async (req, res, next) => {110  try {111    const {name, password} = req.body;112    const {id} = req.params;113
114    const user = await UserService.findById(id);115    if (!user) {116      return res.status(ConstantHttpCode.NOT_FOUND).json({117        status: {118          code: ConstantHttpCode.NOT_FOUND,119          msg: ConstantHttpReason.NOT_FOUND,120        },121        msg: ConstantMessage.USER_NOT_FOUND,122      });123    }124    logger.info(`user ${user.username} found`);125
126    const nameValidated = UserService.validateName(name);127    if (!nameValidated) {128      return res.status(ConstantHttpCode.BAD_REQUEST).json({129        status: {130          code: ConstantHttpCode.BAD_REQUEST,131          msg: ConstantHttpReason.BAD_REQUEST,132        },133        msg: ConstantMessage.NAME_NOT_VALID,134      });135    }136
137    const passwordValidated = UserService.validatePassword(password);138    if (!passwordValidated) {139      return res.status(ConstantHttpCode.BAD_REQUEST).json({140        status: {141          code: ConstantHttpCode.BAD_REQUEST,142          msg: ConstantHttpReason.BAD_REQUEST,143        },144        msg: ConstantMessage.PASSWORD_NOT_VALID,145      });146    }147
148    const isMatch = UserService.comparePassword(password, user.password);149    if (!isMatch) {150      return res.status(ConstantHttpCode.BAD_REQUEST).json({151        status: {152          code: ConstantHttpCode.BAD_REQUEST,153          msg: ConstantHttpReason.BAD_REQUEST,154        },155        msg: ConstantMessage.PASSWORD_NOT_MATCH,156      });157    }158    logger.info(`password ${password} is valid`);159
160    if (user.name === name) {161      return res.status(ConstantHttpCode.BAD_REQUEST).json({162        status: {163          code: ConstantHttpCode.BAD_REQUEST,164          msg: ConstantHttpReason.BAD_REQUEST,165        },166        msg: ConstantMessage.NAME_NOT_CHANGE,167      });168    }169    logger.info(`name ${name} is valid`);170
171    const updatedUser = await UserService.updateName(id, name);172    if (!updatedUser) {173      return res.status(ConstantHttpCode.BAD_REQUEST).json({174        status: {175          code: ConstantHttpCode.BAD_REQUEST,176          msg: ConstantHttpReason.BAD_REQUEST,177        },178        msg: ConstantMessage.NAME_NOT_CHANGE,179      });180    }181    logger.info(`user ${user.username} updated`);182
183    return res.status(ConstantHttpCode.OK).json({184      status: {185        code: ConstantHttpCode.OK,186        msg: ConstantHttpReason.OK,187      },188      msg: ConstantMessage.NAME_CHANGE_SUCCESS,189      data: {190        user: updatedUser,191      },192    });193  } catch (err) {194    return next(err);195  }196};197
198export const updateEmail = async (req, res, next) => {199  try {200    const {email, password} = req.body;201    const {id} = req.params;202
203    const user = await UserService.findById(id);204    if (!user) {205      return res.status(ConstantHttpCode.NOT_FOUND).json({206        status: {207          code: ConstantHttpCode.NOT_FOUND,208          msg: ConstantHttpReason.NOT_FOUND,209        },210        msg: ConstantMessage.USER_NOT_FOUND,211      });212    }213    logger.info(`user ${user.username} found`);214
215    const emailValidated = UserService.validateEmail(email);216    if (!emailValidated) {217      return res.status(ConstantHttpCode.BAD_REQUEST).json({218        status: {219          code: ConstantHttpCode.BAD_REQUEST,220          msg: ConstantHttpReason.BAD_REQUEST,221        },222        msg: ConstantMessage.EMAIL_NOT_VALID,223      });224    }225    logger.info(`email ${email} is valid`);226
227    const passwordValidated = UserService.validatePassword(password);228    if (!passwordValidated) {229      return res.status(ConstantHttpCode.BAD_REQUEST).json({230        status: {231          code: ConstantHttpCode.BAD_REQUEST,232          msg: ConstantHttpReason.BAD_REQUEST,233        },234        msg: ConstantMessage.PASSWORD_NOT_VALID,235      });236    }237    logger.info(`password ${password} is valid`);238
239    const isMatch = UserService.comparePassword(password, user.password);240    if (!isMatch) {241      return res.status(ConstantHttpCode.BAD_REQUEST).json({242        status: {243          code: ConstantHttpCode.BAD_REQUEST,244          msg: ConstantHttpReason.BAD_REQUEST,245        },246        msg: ConstantMessage.PASSWORD_NOT_MATCH,247      });248    }249    logger.info(`password ${password} is valid`);250
251    if (user.email === email) {252      return res.status(ConstantHttpCode.BAD_REQUEST).json({253        status: {254          code: ConstantHttpCode.BAD_REQUEST,255          msg: ConstantHttpReason.BAD_REQUEST,256        },257        msg: ConstantMessage.EMAIL_NOT_CHANGE,258      });259    }260    logger.info(`email ${email} is valid`);261
262    const emailCheck = await UserService.findByEmail(email);263    if (emailCheck) {264      return res.status(ConstantHttpCode.BAD_REQUEST).json({265        status: {266          code: ConstantHttpCode.BAD_REQUEST,267          msg: ConstantHttpReason.BAD_REQUEST,268        },269        msg: ConstantMessage.EMAIL_EXIST,270      });271    }272
273    const updatedUser = await UserService.updateEmail(id, email);274    if (!updatedUser) {275      return res.status(ConstantHttpCode.BAD_REQUEST).json({276        status: {277          code: ConstantHttpCode.BAD_REQUEST,278          msg: ConstantHttpReason.BAD_REQUEST,279        },280        msg: ConstantMessage.EMAIL_NOT_CHANGE,281      });282    }283    logger.info(`user ${user.username} updated`);284
285    return res.status(ConstantHttpCode.OK).json({286      status: {287        code: ConstantHttpCode.OK,288        msg: ConstantHttpReason.OK,289      },290      msg: ConstantMessage.EMAIL_CHANGE_SUCCESS,291      data: {292        user: updatedUser,293      },294    });295  } catch (err) {296    return next(err);297  }298};299
300export const updatePassword = async (req, res, next) => {301  try {302    const {oldPassword, newPassword, confirmPassword} = req.body;303    const {id} = req.params;304
305    if (newPassword !== confirmPassword) {306      return res.status(ConstantHttpCode.BAD_REQUEST).json({307        status: {308          code: ConstantHttpCode.BAD_REQUEST,309          msg: ConstantHttpReason.BAD_REQUEST,310        },311        msg: ConstantMessage.PASSWORD_NOT_MATCH,312      });313    }314
315    const user = await UserService.findById(id);316    if (!user) {317      return res.status(ConstantHttpCode.NOT_FOUND).json({318        status: {319          code: ConstantHttpCode.NOT_FOUND,320          msg: ConstantHttpReason.NOT_FOUND,321        },322        msg: ConstantMessage.USER_NOT_FOUND,323      });324    }325    logger.info(`user ${user.username} found`);326
327    const oldPasswordValidated = UserService.validatePassword(oldPassword);328    if (!oldPasswordValidated) {329      return res.status(ConstantHttpCode.BAD_REQUEST).json({330        status: {331          code: ConstantHttpCode.BAD_REQUEST,332          msg: ConstantHttpReason.BAD_REQUEST,333        },334        msg: ConstantMessage.PASSWORD_NOT_VALID,335      });336    }337    logger.info(`password ${oldPassword} is valid`);338
339    const newPasswordValidated = UserService.validatePassword(newPassword);340    if (!newPasswordValidated) {341      return res.status(ConstantHttpCode.BAD_REQUEST).json({342        status: {343          code: ConstantHttpCode.BAD_REQUEST,344          msg: ConstantHttpReason.BAD_REQUEST,345        },346        msg: ConstantMessage.PASSWORD_NOT_VALID,347      });348    }349    logger.info(`password ${newPassword} is valid`);350
351    const confirmPasswordValidated =352      UserService.validatePassword(confirmPassword);353    if (!confirmPasswordValidated) {354      return res.status(ConstantHttpCode.BAD_REQUEST).json({355        status: {356          code: ConstantHttpCode.BAD_REQUEST,357          msg: ConstantHttpReason.BAD_REQUEST,358        },359        msg: ConstantMessage.PASSWORD_NOT_VALID,360      });361    }362    logger.info(`password ${confirmPassword} is valid`);363
364    if (oldPassword === newPassword) {365      return res.status(ConstantHttpCode.BAD_REQUEST).json({366        status: {367          code: ConstantHttpCode.BAD_REQUEST,368          msg: ConstantHttpReason.BAD_REQUEST,369        },370        msg: ConstantMessage.PASSWORD_NOT_CHANGE,371      });372    }373
374    const isMatch = UserService.comparePassword(oldPassword, user.password);375    if (!isMatch) {376      return res.status(ConstantHttpCode.BAD_REQUEST).json({377        status: {378          code: ConstantHttpCode.BAD_REQUEST,379          msg: ConstantHttpReason.BAD_REQUEST,380        },381        msg: ConstantMessage.PASSWORD_NOT_MATCH,382      });383    }384
385    const updatedUser = await UserService.updatePassword(id, newPassword);386    if (!updatedUser) {387      return res.status(ConstantHttpCode.BAD_REQUEST).json({388        status: {389          code: ConstantHttpCode.BAD_REQUEST,390          msg: ConstantHttpReason.BAD_REQUEST,391        },392        msg: ConstantMessage.PASSWORD_NOT_CHANGE,393      });394    }395    logger.info(`user ${user.username} updated`);396
397    return res.status(ConstantHttpCode.OK).json({398      status: {399        code: ConstantHttpCode.OK,400        msg: ConstantHttpReason.OK,401      },402      msg: ConstantMessage.PASSWORD_CHANGE_SUCCESS,403      data: {404        user: updatedUser,405      },406    });407  } catch (err) {408    return next(err);409  }410};411
412export const updatePhone = async (req, res, next) => {413  try {414    const {phone, password} = req.body;415    const {id} = req.params;416
417    const user = await UserService.findById(id);418    if (!user) {419      return res.status(ConstantHttpCode.NOT_FOUND).json({420        status: {421          code: ConstantHttpCode.NOT_FOUND,422          msg: ConstantHttpReason.NOT_FOUND,423        },424        msg: ConstantMessage.USER_NOT_FOUND,425      });426    }427    logger.info(`user ${user.username} found`);428
429    const phoneValidated = UserService.validatePhone(phone);430    if (!phoneValidated) {431      return res.status(ConstantHttpCode.BAD_REQUEST).json({432        status: {433          code: ConstantHttpCode.BAD_REQUEST,434          msg: ConstantHttpReason.BAD_REQUEST,435        },436        msg: ConstantMessage.PHONE_NOT_VALID,437      });438    }439
440    const passwordValidated = UserService.validatePassword(password);441    if (!passwordValidated) {442      return res.status(ConstantHttpCode.BAD_REQUEST).json({443        status: {444          code: ConstantHttpCode.BAD_REQUEST,445          msg: ConstantHttpReason.BAD_REQUEST,446        },447        msg: ConstantMessage.PASSWORD_NOT_VALID,448      });449    }450    logger.info(`password ${password} is valid`);451
452    const isMatch = UserService.comparePassword(password, user.password);453    if (!isMatch) {454      return res.status(ConstantHttpCode.BAD_REQUEST).json({455        status: {456          code: ConstantHttpCode.BAD_REQUEST,457          msg: ConstantHttpReason.BAD_REQUEST,458        },459        msg: ConstantMessage.PASSWORD_NOT_MATCH,460      });461    }462    logger.info(`password ${password} is valid`);463
464    if (user.phone === phone) {465      return res.status(ConstantHttpCode.BAD_REQUEST).json({466        status: {467          code: ConstantHttpCode.BAD_REQUEST,468          msg: ConstantHttpReason.BAD_REQUEST,469        },470        msg: ConstantMessage.PHONE_NOT_CHANGE,471      });472    }473
474    const phoneCheck = await UserService.findByPhone(phone);475    if (phoneCheck) {476      return res.status(ConstantHttpCode.BAD_REQUEST).json({477        status: {478          code: ConstantHttpCode.BAD_REQUEST,479          msg: ConstantHttpReason.BAD_REQUEST,480        },481        msg: ConstantMessage.PHONE_EXIST,482      });483    }484
485    const updatedUser = await UserService.updatePhone(id, phone);486    if (!updatedUser) {487      return res.status(ConstantHttpCode.BAD_REQUEST).json({488        status: {489          code: ConstantHttpCode.BAD_REQUEST,490          msg: ConstantHttpReason.BAD_REQUEST,491        },492        msg: ConstantMessage.PHONE_NOT_CHANGE,493      });494    }495    logger.info(`user ${user.username} updated`);496
497    return res.status(ConstantHttpCode.OK).json({498      status: {499        code: ConstantHttpCode.OK,500        msg: ConstantHttpReason.OK,501      },502      msg: ConstantMessage.PHONE_CHANGE_SUCCESS,503      data: {504        user: updatedUser,505      },506    });507  } catch (err) {508    return next(err);509  }510};511
512export const updateAddress = async (req, res, next) => {513  try {514    const {address, password} = req.body;515    const {id} = req.params;516
517    const user = await UserService.findById(id);518    if (!user) {519      return res.status(ConstantHttpCode.NOT_FOUND).json({520        status: {521          code: ConstantHttpCode.NOT_FOUND,522          msg: ConstantHttpReason.NOT_FOUND,523        },524        msg: ConstantMessage.USER_NOT_FOUND,525      });526    }527    logger.info(`user ${user.username} found`);528
529    const addressValidated = UserService.validateAddress(address);530    if (!addressValidated) {531      return res.status(ConstantHttpCode.BAD_REQUEST).json({532        status: {533          code: ConstantHttpCode.BAD_REQUEST,534          msg: ConstantHttpReason.BAD_REQUEST,535        },536        msg: ConstantMessage.ADDRESS_NOT_VALID,537      });538    }539
540    const isMatch = UserService.comparePassword(password, user.password);541    if (!isMatch) {542      return res.status(ConstantHttpCode.BAD_REQUEST).json({543        status: {544          code: ConstantHttpCode.BAD_REQUEST,545          msg: ConstantHttpReason.BAD_REQUEST,546        },547        msg: ConstantMessage.PASSWORD_NOT_MATCH,548      });549    }550    logger.info(`password ${password} is valid`);551
552    if (user.address === address) {553      return res.status(ConstantHttpCode.BAD_REQUEST).json({554        status: {555          code: ConstantHttpCode.BAD_REQUEST,556          msg: ConstantHttpReason.BAD_REQUEST,557        },558        msg: ConstantMessage.ADDRESS_NOT_CHANGE,559      });560    }561
562    const updatedUser = await UserService.updateAddress(id, address);563    if (!updatedUser) {564      return res.status(ConstantHttpCode.BAD_REQUEST).json({565        status: {566          code: ConstantHttpCode.BAD_REQUEST,567          msg: ConstantHttpReason.BAD_REQUEST,568        },569        msg: ConstantMessage.ADDRESS_NOT_CHANGE,570      });571    }572    logger.info(`user ${user.username} updated`);573
574    return res.status(ConstantHttpCode.OK).json({575      status: {576        code: ConstantHttpCode.OK,577        msg: ConstantHttpReason.OK,578      },579      msg: ConstantMessage.ADDRESS_CHANGE_SUCCESS,580      data: {581        user: updatedUser,582      },583    });584  } catch (err) {585    return next(err);586  }587};588
589export const deleteUser = async (req, res, next) => {590  try {591    const {id} = req.params;592
593    const user = await UserService.findById(id);594    if (!user) {595      return res.status(ConstantHttpCode.NOT_FOUND).json({596        status: {597          code: ConstantHttpCode.NOT_FOUND,598          msg: ConstantHttpReason.NOT_FOUND,599        },600        msg: ConstantMessage.USER_NOT_FOUND,601      });602    }603    logger.info(`user ${user.username} found`);604
605    const deletedUser = await UserService.deleteUser(id);606    if (!deletedUser) {607      return res.status(ConstantHttpCode.BAD_REQUEST).json({608        status: {609          code: ConstantHttpCode.BAD_REQUEST,610          msg: ConstantHttpReason.BAD_REQUEST,611        },612        msg: ConstantMessage.USER_NOT_DELETE,613      });614    }615    logger.info(`user ${user.username} deleted`);616
617    return res.status(ConstantHttpCode.OK).json({618      status: {619        code: ConstantHttpCode.OK,620        msg: ConstantHttpReason.OK,621      },622      msg: ConstantMessage.USER_DELETE_SUCCESS,623    });624  } catch (err) {625    return next(err);626  }627};628
629export const getUser = async (req, res, next) => {630  try {631    const {id} = req.params;632    logger.info(`user ${id} found`);633
634    const user = await UserService.findByIdWithOutPassword(id);635    logger.info(`user ${user} found`);636    if (!user) {637      return res.status(ConstantHttpCode.NOT_FOUND).json({638        status: {639          code: ConstantHttpCode.NOT_FOUND,640          msg: ConstantHttpReason.NOT_FOUND,641        },642        msg: ConstantMessage.USER_NOT_FOUND,643      });644    }645    logger.info(`user ${user.username} found`);646
647    return res.status(ConstantHttpCode.OK).json({648      status: {649        code: ConstantHttpCode.OK,650        msg: ConstantHttpReason.OK,651      },652      msg: ConstantMessage.USER_FOUND,653      data: {654        user,655      },656    });657  } catch (err) {658    return next(err);659  }660};661
662export const getUsers = async (req, res, next) => {663  try {664    const users = await UserService.findAll();665    if (!users) {666      return res.status(ConstantHttpCode.NOT_FOUND).json({667        status: {668          code: ConstantHttpCode.NOT_FOUND,669          msg: ConstantHttpReason.NOT_FOUND,670        },671        msg: ConstantMessage.USER_NOT_FOUND,672      });673    }674    logger.info(`users found`);675
676    return res.status(ConstantHttpCode.OK).json({677      status: {678        code: ConstantHttpCode.OK,679        msg: ConstantHttpReason.OK,680      },681      msg: ConstantMessage.USER_FOUND,682      data: {683        users,684      },685    });686  } catch (err) {687    return next(err);688  }689};690
691export const getUsersStats = async (req, res, next) => {692  try {693    const usersStats = await UserService.getUsersStats();694    if (!usersStats) {695      return res.status(ConstantHttpCode.NOT_FOUND).json({696        status: {697          code: ConstantHttpCode.NOT_FOUND,698          msg: ConstantHttpReason.NOT_FOUND,699        },700        msg: ConstantMessage.USER_NOT_FOUND,701      });702    }703    logger.info(`users stats found`);704
705    return res.status(ConstantHttpCode.OK).json({706      status: {707        code: ConstantHttpCode.OK,708        msg: ConstantHttpReason.OK,709      },710      msg: ConstantMessage.USER_FOUND,711      data: {712        users: usersStats,713      },714    });715  } catch (err) {716    return next(err);717  }718};719
720export default {721  updateUsername,722  updateName,723  updateEmail,724  updatePassword,725  updatePhone,726  updateAddress,727  deleteUser,728  getUser,729  getUsers,730  getUsersStats,731};
```

### [Authentication routes](#authentication-routes)

`auth.router.js`

```
1import ConstantAPI from '../constants/api.constant';2import AuthController from '../controllers/auth.controller';3import express from 'express';4
5const router = express.Router();6
7router.post(ConstantAPI.AUTH_REGISTER, AuthController.register);8router.post(ConstantAPI.AUTH_LOGIN, AuthController.login);9
10export default router;
```

`user.router.js`

```
1import ConstantAPI from '../constants/api.constant';2import UserController from '../controllers/user.controller';3import TokenMiddleware from '../middlewares/token.middleware';4import express from 'express';5
6const router = express.Router();7
8router.post(9  ConstantAPI.USER_UPDATE_USERNAME,10  TokenMiddleware.verifyTokenAndAuthorization,11  UserController.updateUsername,12);13router.post(14  ConstantAPI.USER_UPDATE_NAME,15  TokenMiddleware.verifyTokenAndAuthorization,16  UserController.updateName,17);18router.post(19  ConstantAPI.USER_UPDATE_EMAIL,20  TokenMiddleware.verifyTokenAndAuthorization,21  UserController.updateEmail,22);23router.post(24  ConstantAPI.USER_UPDATE_PASSWORD,25  TokenMiddleware.verifyTokenAndAuthorization,26  UserController.updatePassword,27);28router.post(29  ConstantAPI.USER_UPDATE_PHONE,30  TokenMiddleware.verifyTokenAndAuthorization,31  UserController.updatePhone,32);33router.post(34  ConstantAPI.USER_UPDATE_ADDRESS,35  TokenMiddleware.verifyTokenAndAuthorization,36  UserController.updateAddress,37);38router.post(39  ConstantAPI.USER_DELETE,40  TokenMiddleware.verifyTokenAndAuthorization,41  UserController.deleteUser,42);43router.get(44  ConstantAPI.USER_GET,45  TokenMiddleware.verifyTokenAndAuthorization,46  UserController.getUser,47);48router.get(49  ConstantAPI.USER_GET_ALL,50  TokenMiddleware.verifyTokenAndAdmin,51  UserController.getUsers,52);53router.get(54  ConstantAPI.USER_GET_ALL_STATS,55  TokenMiddleware.verifyTokenAndAdmin,56  UserController.getUsersStats,57);58
59export default router;
```

edit `index.js`

```
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';7import {DATABASE_URL} from './env/variable.env';8// routers9import AuthRouter from './routers/auth.router';10import UserRouter from './routers/user.router';11import compression from 'compression';12import cookieParser from 'cookie-parser';13import cors from 'cors';14import express from 'express';15import helmet from 'helmet';16
17connectDb(DATABASE_URL);18
19const app = express();20
21// helmet22app.use(helmet());23
24app.use(express.urlencoded({extended: true}));25app.use(express.json());26app.use(compression());27app.use(cors());28app.use(cookieParser());29
30app.get('/', (req, res, next) => {31  try {32    return res.status(ConstantHttpCode.OK).json({33      status: {34        code: ConstantHttpCode.OK,35        msg: ConstantHttpReason.OK,36      },37      API: 'Work',38    });39  } catch (err) {40    return next(err);41  }42});43
44app.use(ConstantAPI.API_AUTH, AuthRouter);45app.use(ConstantAPI.API_USERS, UserRouter);46
47export default app;
```

## [Frequently asked questions](#frequently-asked-questions)

Babel lets you write modern ES module syntax (`import`/`export`) and the latest ECMAScript features, then transpile down to JavaScript your target Node version understands. You get one consistent, future-proof syntax across the codebase and a single `pnpm build` step that emits a deployable `build/` directory.

Yes. Every `pnpm add` maps to `npm install` / `yarn add`, and the `package.json` scripts run the same way with `npm run <script>` or `yarn <script>`. The `engine-strict` setting in `.npmrc` plus the `engines.pnpm` field just nudge contributors toward the package manager you standardize on.

The `pre-commit` hook runs `pnpm lint`, so a commit fails if ESLint reports errors. The `pre-push` hook runs `pnpm build`, so you can’t push code that doesn’t compile. The `commit-msg` hook runs Commitlint, so commit messages must follow the conventional-commits format.

Winston gives you log levels, multiple transports (console plus rotating files), and structured timestamps so development output stays readable while errors persist to `logs/error.log` in production. `console.log` can’t do level filtering or file output without extra work.

`.env` is git-ignored, so the secret never lands in version control. That’s the whole point of the `.env` / `.env.example` split. In production, inject `JWT_SECRET` and `PASS_SECRET` through your host’s environment or a secrets manager rather than a committed file.

## [Summary](#summary)

Finally, after compilation, we need to deploy the compiled version to the NodeJS production server.

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

## [References](#references)

-   [Node.js Official Website](https://nodejs.org/)
-   [Express.js Official Website](https://expressjs.com/)
-   [MongoDB Official Website](https://www.mongodb.com/)
-   [Mongoose ODM Official Website](https://mongoosejs.com/)
-   [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/)
-   [Commitlint Official Documentation](https://commitlint.js.org/)
-   [JSON Web Tokens (JWT) Official Site](https://jwt.io/)
-   [Winston Logger (GitHub)](https://github.com/winstonjs/winston)
-   [Dotenv (npm package)](https://www.npmjs.com/package/dotenv)
-   [tsx (npm package)](https://www.npmjs.com/package/tsx)
-   [Helmet (npm package)](https://www.npmjs.com/package/helmet)
-   [CryptoJS (Google Code Archive - for historical reference, often found on npm)](https://www.npmjs.com/package/crypto-js)
-   [Git SSH Keys for GitHub, GitLab, and Bitbucket on Linux](/blog/post/git-ssh-keys-for-github-gitlab-and-bitbucket-on-linux)
-   [Dotfiles: A Git-Based Strategy for Configuration Management](/blog/post/dotfiles)

Was this useful?

## Tags

[#Node.js](/blog/tags/nodejs)[#Express.js](/blog/tags/expressjs)[#MongoDB](/blog/tags/mongodb)[#Babel](/blog/tags/babel)[#ESLint](/blog/tags/eslint)[#Prettier](/blog/tags/prettier)[#Husky](/blog/tags/husky)[#JWT Authentication](/blog/tags/jwt-authentication)[#API Security](/blog/tags/api-security)[#Development Workflow](/blog/tags/development-workflow)[#JavaScript Backend](/blog/tags/javascript-backend)[#Project Setup](/blog/tags/project-setup)[#Winston Logger](/blog/tags/winston-logger)[#Git Hooks](/blog/tags/git-hooks)

## Share

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

## 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 JWT Authentication in TypeScript with Express, MongoDB, Babel, Prettier, ESLint, and Husky - Part 2](/_astro/hero.DKzl3k6w_w3X8j.webp)](/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](/blog/post/setting-up-jwt-authentication-in-typescript-with-express-mongodb-babel-prettier-eslint-and-husky-part-2)

-   [Mohammad Abu Mattar](/authors/mohammad-abu-mattar)
-   [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)

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 you

[#JWT](/blog/tags/jwt)[#Express.js](/blog/tags/expressjs)[#MongoDB](/blog/tags/mongodb)+10 tags

[read more](/blog/post/setting-up-jwt-authentication-in-typescript-with-express-mongodb-babel-prettier-eslint-and-husky-part-2)

[![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)

[![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)

[![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
