DEV Community

Arsalan Ahmed Yaldram
Arsalan Ahmed Yaldram

Posted on

Setup express with Typescript - base project setup

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.

Step One: Bootstrap the project

Let us start by bootstrapping our project. First create a new directory and run npm init.

mkdir express-starter-ts
cd express-starter-ts
npm init
Enter fullscreen mode Exit fullscreen mode

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 : -

npm install --save-dev typescript ts-node nodemon
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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 : -

npx tsc --init
Enter fullscreen mode Exit fullscreen mode

The above command will generate a tsconfig.json file you can customize it as you like. Here is my tsconfig.json -

{
  "compilerOptions": {
    "target": "es2016",
    "module": "commonjs",
    "esModuleInterop": true,
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "forceConsistentCasingInFileNames": true,
    "strict": true,
    "strictPropertyInitialization": false,
    "outDir": "build",
    "rootDir": "./"
  },
  "exclude": ["build", "tests"]
}
Enter fullscreen mode Exit fullscreen mode

Finally install expressjs in your terminal run the following -

npm install express
Enter fullscreen mode Exit fullscreen mode

Also install the associated types for expressjs like so -

npm install --save-dev @types/express
Enter fullscreen mode Exit fullscreen mode

In your package.json add the following under the scripts section -

 "scripts": {
    "build": "tsc -p tsconfig.json",
    "dev": "nodemon --exec ts-node src/app.ts",
    "start": "node build/src/app.js"
  }
Enter fullscreen mode Exit fullscreen mode

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 -

const app = express(); // initialize express app
app.listen(PORT_NO, () => {}) // boot the express app
Enter fullscreen mode Exit fullscreen mode
  • 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 -

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;
Enter fullscreen mode Exit fullscreen mode

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 : -

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();
Enter fullscreen mode Exit fullscreen mode

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.

npm run dev
Enter fullscreen mode Exit fullscreen mode

Step Four: Set up linting

Install the necessary dependencies in your terminal run -

npm install --save-dev eslint eslint-config-prettier eslint-plugin-prettier prettier
Enter fullscreen mode Exit fullscreen mode

Then setup eslint, by running the following in your terminal -

npx eslint --init
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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 -

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',
 },
}
Enter fullscreen mode Exit fullscreen mode

In the root of your project create a new file .prettierrc -

{
  "trailingComma": "es5",
  "tabWidth": 2,
  "semi": true,
  "singleQuote": true
}
Enter fullscreen mode Exit fullscreen mode

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. With husky we use another package called nano-staged. In your terminal run the following command -

npx husky-init && npm install
npm install --save-dev nano-staged
Enter fullscreen mode Exit fullscreen mode

Now lets add a husky hook in your terminal run :

 npx husky add .husky/pre-commit "npx nano-staged"
Enter fullscreen mode Exit fullscreen mode

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 : -

"nano-staged": {
    "*.{js,ts}": "prettier --write"
 },
Enter fullscreen mode Exit fullscreen mode

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 : -

 "lint": "eslint . --color",
 "lint:fix": "eslint . --fix",
 "pretty": "prettier . --write",
Enter fullscreen mode Exit fullscreen mode

Now lets commit our files to see husky hooks running -

git add .
git commit -m "feat: express typescript project setup"
Enter fullscreen mode Exit fullscreen mode

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 -

npm install --save-dev git-cz commitizen
npx commitizen init cz-conventional-changelog --save-dev --save-exact
Enter fullscreen mode Exit fullscreen mode

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

"config": {
   "commitizen": {
      "path": "git-cz"
   }
 }
Enter fullscreen mode Exit fullscreen mode

Add the following under the scripts section in package.json : -

 "commit": "git-cz"
Enter fullscreen mode Exit fullscreen mode

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

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',
    },
  },
};
Enter fullscreen mode Exit fullscreen mode

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

git add .
npm run commit
Enter fullscreen mode Exit fullscreen mode

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. In the next tutorial we will create our routes and controllers until next time PEACE.

Top comments (0)