Setup express with Typescript - base project setup
Tags: javascript, typescript, node
Published: 2022-07-12 07:01:43

### Introduction

In this series we will setup an express server using `Typescript`, we will be using `TypeOrm` as our ORM for querying a PostgresSql Database, we will also use `Jest` and `SuperTest` for testing. The goal of this series is not to create a full-fledged node backend but to setup an express starter project using typescript which can be used as a starting point if you want to develop a node backend using express and typescript.

### Overview

This series is not recommended for beginners some familiarity and experience working with `nodejs`, `express`, `typescript` and `typeorm` is expected. In this post which is part one of our series we will start from scratch, we will cover the following : - 
 - Bootstrap our node project.
 - Setup typescript and install express.
 - Initialize expressjs.
 - Setup eslint.
 - Setup husky precommit hook.
 - Setup git-cz commitizen.

This tutorial covers a lot, but no worries all the code is available under the `base-express-setup` branch, check the [repo](https://github.com/yaldram/express-typescript-starter/).

### Step One: Bootstrap the project

Let us start by bootstrapping our project. First create a new directory and run `npm init`.
```bash
mkdir express-starter-ts
cd express-starter-ts
npm init
```
After you finish executing `npm init` command you have the `package.json` file in your project. Now let us install some dev dependencies run the following in your terminal : - 
```bash
npm install --save-dev typescript ts-node nodemon
```
We have have installed `nodemon` so that whenever we make any change in our files the server starts itself without our intervention. We have installed `ts-node` so that our typescript code is compiled on the fly as we develop, and finally typescript.

Let us also create a `.gitignore` file and add the following - 
```
.ionide

.env

node_modules

build
```

### Step Two: Setup typescript and install express

Now that we have typescript we need to set it up, run the following command in your terminal : - 
```bash
npx tsc --init
```
The above command will generate a `tsconfig.json` file you can customize it as you like. Here is my `tsconfig.json` - 
```json
{
  "compilerOptions": {
    "target": "es2016",
    "module": "commonjs",
    "esModuleInterop": true,
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "forceConsistentCasingInFileNames": true,
    "strict": true,
    "strictPropertyInitialization": false,
    "outDir": "build",
    "rootDir": "./"
  },
  "exclude": ["build", "tests"]
}
```

Finally install `expressjs` in your terminal run the following - 
```bash
npm install express
```
Also install the associated types for `expressjs` like so - 
```bash
npm install --save-dev @types/express
```
In your `package.json` add the following under the scripts section - 
```json
 "scripts": {
    "build": "tsc -p tsconfig.json",
    "dev": "nodemon --exec ts-node src/app.ts",
    "start": "node build/src/app.js"
  }
```

### Step Three: Setup express js

Now we will create our `app.ts` file and setup `expressjs`. Lets create a `src` folder in our project and inside the `src` folder we will create two files namely `app.ts` and `server.ts`. What's the difference you might ask ? Don't we just create `app.ts` as an entry point and boot our express server like - 
```js
const app = express(); // initialize express app
app.listen(PORT_NO, () => {}) // boot the express app
```
- But we will initialize our express app in `server.ts` file, we will add our middlewares, routes here. 
- We will start / boot our application in the `app.ts` file our entry point. 
- Well this separation is very important, when you want to boot the express server during integration tests. We will cover this when we setup testing later.

In your `server.ts` paste the following code - 

```typescript
import express from "express";

export class HttpServer {
  public app: express.Application;

  constructor() {
    this.app = express();
    this.addMiddlewares();
    this.addRoutes();
  }

  // configure middlewares for express
  private addMiddlewares() {
    // for parsing application/json
    this.app.use(express.json());
    // for parsing application/x-www-form-urlencoded
    this.app.use(express.urlencoded({ extended: true }));
  }

  // configure routes for express
  private addRoutes() {
    this.app.get("/", (req, res) => {
      return res.status(200).json({
        status: true,
        statusCode: 200,
        message: "Success",
      });
    });
  }
}

const expressServer = new HttpServer();
export default expressServer.app;
```
The code is very simple to understand in the constructor we created an express app and added some middlewares and routes. Just remove all the `class and class methods` you will find it very similar to what we normally do in javascript. Finally we create an object `const expressServer = new HttpServer()` and export default our express app which is a property of the `expressServer object`.

Now under app.ts paste the following code : - 

```typescript
import * as http from "http";

import expressServer from "./server";

class Main {
  private readonly port = 8080;
  private server: http.Server;

  constructor() {
    this.server = http.createServer(expressServer);
    this.startServer();
  }

  private startServer() {
    this.server.listen(this.port, async () => {
      console.log("Server started on port", this.port);
    });
  }
}

new Main();
```
Again very straightforward, to `http.createServer` we pass our express app and then just boot our server using `server.listen()`. If you are wondering about environment variables and graceful shutdowns don't worry we will cover all of them in the future.

With that lets run our app. In your terminal run the following command and visit `http://localhost:8080/` you will see the success response.  
```bash
npm run dev
```

### Step Four: Set up linting

Install the necessary dependencies in your terminal run - 
```bash
npm install --save-dev eslint eslint-config-prettier eslint-plugin-prettier prettier
```
Then setup eslint, by running the following in your terminal - 
```bash
npx eslint --init
```
The above command will open up a questionnaire fill it like so - 
```
✔ How would you like to use ESLint? - To check syntax, find problems, and enforce code style
✔ What type of modules does your project use? - JavaScript modules (import/export)
✔ Which framework does your project use? - None of these
✔ Does your project use TypeScript? - Yes
✔ Where does your code run? - Node
✔ How would you like to define a style for your project?  Answer questions
✔ What format do you want your config file to be in? - JavaScript
✔ What style of indentation do you use? Tab
✔ What quotes do you use for strings? Single
✔ What line endings do you use?  Unix
✔ Do you require semicolons? No
```
Eslint will also prompt you to install `@typescript-eslint/eslint-plugin@latest @typescript-eslint/parser@latest` during the questionnaire, make sure you install the packages. With this we have finished setting up linting for our project. 

Here is my final `.eslintrc.js` file - 
```javascript
module.exports = {
 env: {
  node: true,
  es2021: true,
 },
 extends: ['eslint:recommended', 'plugin:@typescript- eslint/recommended', 'prettier',],
 parser: '@typescript-eslint/parser',
 parserOptions: {
  ecmaVersion: 'latest',
  sourceType: 'module',
 },
 plugins: ['@typescript-eslint', 'prettier'],
 rules: {
   'prettier/prettier': 'error',
 },
}
``` 

In the root of your project create a new file `.prettierrc` - 
```json
{
  "trailingComma": "es5",
  "tabWidth": 2,
  "semi": true,
  "singleQuote": true
}
```

### Step Five: Set up husky hooks

`Husky hooks` are a great way to run linting and other quality checks when we commit our code. If you are not familiar with husky hooks I recommend you to check it [here] (https://www.freecodecamp.org/news/how-to-add-commit-hooks-to-git-with-husky-to-automate-code-tasks/). With husky we use another package called `nano-staged`. In your terminal run the following command - 
```bash
npx husky-init && npm install
npm install --save-dev nano-staged
```
Now lets add a husky hook in your terminal run : 
```bash
 npx husky add .husky/pre-commit "npx nano-staged"
```
Running the above command will create a `.husky` folder in our project in the `.husky` folder you fill find `pre-commit` file open it and check if it has the `npx nano-staged` command remove the `npm test` command if it exists.

We are telling husky to run `npx nano-staged` after we run `git commit`, now let us add the `nano-staged` command in the package.json like so : - 
```json
"nano-staged": {
    "*.{js,ts}": "prettier --write"
 },
```
In the nano-staged command we are basically running prettier on our staged files.

Also in the `package.json` file under the scripts section add the following scripts : - 
```json
 "lint": "eslint . --color",
 "lint:fix": "eslint . --fix",
 "pretty": "prettier . --write",
```

Now lets commit our files to see husky hooks running - 
```bash
git add .
git commit -m "feat: express typescript project setup"
```

### Step Six: Setup git-cz, commitizen (Optional)

I like to write git commits using `git-cz` and `commitizen` this step is optional you can skip it. Let us first install the necessary packages, run the following commands in your terminal - 
```bash
npm install --save-dev git-cz commitizen
npx commitizen init cz-conventional-changelog --save-dev --save-exact
```

In our package.json replace the default commitizen configuration with the one below : - 

```json
"config": {
   "commitizen": {
      "path": "git-cz"
   }
 }
```

Add the following under the scripts section in `package.json` : - 
```json
 "commit": "git-cz"
```

Now under the root of our project add a file called `changelog.config.js` and paste the following : 

```js
module.exports = {
  disableEmoji: false,
  list: [
    'test',
    'feat',
    'fix',
    'chore',
    'docs',
    'refactor',
    'style',
    'ci',
    'perf',
  ],
  maxMessageLength: 64,
  minMessageLength: 3,
  questions: [
    'type',
    'scope',
    'subject',
    'body',
    'breaking',
    'issues',
    'lerna',
  ],
  scopes: [],
  types: {
    chore: {
      description: 'Build process or auxiliary tool changes',
      emoji: '🤖',
      value: 'chore',
    },
    ci: {
      description: 'CI related changes',
      emoji: '🎡',
      value: 'ci',
    },
    docs: {
      description: 'Documentation only changes',
      emoji: '✏️',
      value: 'docs',
    },
    feat: {
      description: 'A new feature',
      emoji: '🎸',
      value: 'feat',
    },
    fix: {
      description: 'A bug fix',
      emoji: '🐛',
      value: 'fix',
    },
    perf: {
      description: 'A code change that improves performance',
      emoji: '⚡️',
      value: 'perf',
    },
    refactor: {
      description: 'A code change that neither fixes a bug or adds a feature',
      emoji: '💡',
      value: 'refactor',
    },
    release: {
      description: 'Create a release commit',
      emoji: '🏹',
      value: 'release',
    },
    style: {
      description: 'Markup, white-space, formatting, missing semi-colons...',
      emoji: '💄',
      value: 'style',
    },
    test: {
      description: 'Adding missing tests',
      emoji: '💍',
      value: 'test',
    },
  },
};
```

Now when you want to commit your files you would do : - 

```bash
git add .
npm run commit
```

### Summary
There you go our basic project setup is completed. All the code for this tutorial can be found under the `base-express-setup` branch [here](https://github.com/yaldram/express-typescript-starter/tree/feat/base-express-setup). In the next tutorial we will create our routes and controllers until next time PEACE.