(Rotterdam, The Netherlands)
Back to posts
That's me at Valassis office. I commute everyday to the office on a bicycle and I recently bought this awesome helmet that I'm proud of :D Just to clarify, I'm not usually working in a helmet on, tough.

That's me at Valassis office. I commute everyday to the office on a bicycle and I recently bought this awesome helmet that I'm proud of :D Just to clarify, I'm not usually working in a helmet on, tough.

Express with TypeScript setup

20 Sep 2019

I will walk you through setting up basic Express server with TypeScript. By “basic” I mean:

  • eslint + prettier config
  • testing setup (mocha, chai, supertest)
  • alias for local dependencies, so that you can import modules from root
  • watch mode for faster development
  • TypeScript setup
  • VSCode workspace settings

I assume you have node and typescript globally installed.

Let's create our directory:

mkdir express-boilerplate && cd express-boilerplate

Set up git:

git init

Set up npm:

npm init -y

Add ./.gitignore file:

logs
*.log
npm-debug.log*
pids
*.pid
*.seed
*.pid.lock
node_modules
*.tsbuildinfo
.env
build

Add all dependencies we will need:

Dependencies:

  • body-parser
  • express
npm i --save body-parser express

Dev dependencies:

  • @types/body-parser
  • @types/express
  • @typescript-eslint/eslint-plugin
  • @typescript-eslint/parser
  • chai, @types/chai
  • dotenv, @types/dotenv
  • eslint
  • eslint-config-prettier
  • eslint-plugin-prettier
  • mocha, @types/mocha
  • nodemon
  • prettier
  • supertest, @types/supertest
  • ts-node
  • tsconfig-paths
  • typescript
npm i -save-dev @types/body-parser @types/chai @types/dotenv @types/express @types/mocha @types/supertest @typescript-eslint/eslint-plugin @typescript-eslint/parser chai dotenv eslint eslint-config-prettier eslint-plugin-prettier mocha nodemon prettier supertest ts-node tsconfig-paths typescript

Add VSCode settings in ./.vscode/settings.json file:

{
  "editor.tabSize": 2,
  "editor.detectIndentation": false,
  "editor.renderWhitespace": "boundary",
  "files.exclude": {
    "build": true
  },
  "eslint.autoFixOnSave": true,
  "eslint.validate": [
    "javascript",
    "javascriptreact",
    { "language": "typescript", "autoFix": true },
    { "language": "typescriptreact", "autoFix": true }
  ]
}

Make sure you have these extensions in your VSCode installed:

  • ESLint (dbaeumer.vscode-eslint)
  • Prettier — Code formatter (esbenp.prettier-vscode)

Add ./.eslintrc.js config file:

module.exports = {
  parser: "@typescript-eslint/parser",
  extends: [
    "plugin:@typescript-eslint/recommended",
    "prettier/@typescript-eslint",
    "plugin:prettier/recommended",
  ],
  parserOptions: {
    ecmaVersion: 2018,
    sourceType: "module",
  },
  rules: {
    "@typescript-eslint/explicit-function-return-type": 0,
  },
};

Add ./.prettierrc.js config file (use settings you prefer):

module.exports = {
  semi: true,
  trailingComma: "all",
  singleQuote: true,
  printWidth: 120,
  tabWidth: 2,
};

Add ./tsconfig.json file:

{
  "compilerOptions": {
    "module": "commonjs",
    "esModuleInterop": true,
    "target": "es6",
    "noImplicitAny": true,
    "moduleResolution": "node",
    "sourceMap": true,
    "outDir": "build",
    "baseUrl": "src",
    "paths": {
      "*": ["node_modules/*"],
      "src/*": ["./*"]
    }
  },
  "include": ["src/**/*"]
}

Add ./tsconfig-paths-bootstrap.js file. This will allow us to properly build our app using root import alias:

/* eslint-disable @typescript-eslint/no-var-requires */
const tsConfig = require("./tsconfig.json");
const tsConfigPaths = require("tsconfig-paths");
const baseUrl = "./build";
tsConfigPaths.register({
  baseUrl,
  paths: tsConfig.compilerOptions.paths,
});

Edit “main”, “scripts” and “nodemonConfig” section of our ./package.json file:

{
  "main": "build/index.js",
  "scripts": {
    "build": "tsc",
    "start": "node -r ./tsconfig-paths-bootstrap.js .",
    "start:dev": "node -r dotenv/config -r tsconfig-paths/register -r ts-node/register ./src/index.ts",
    "dev": "nodemon",
    "test:unit": "mocha --recursive -r tsconfig-paths/register -r ts-node/register -r source-map-support/register src/**/*.spec.ts",
    "test:lint": "eslint --ext .ts ./src",
    "test:lint:fix": "npm run test:lint -- --fix",
    "test": "npm run test:lint && npm run test:unit"
  },
  "nodemonConfig": {
    "ignore": ["**/*.spec.ts", ".git", "node_modules"],
    "watch": ["src"],
    "exec": "npm run start:dev",
    "ext": "ts"
  }
}

Add a ./src/configuration/index.ts file where we will keep all our ENV variables:

export const PORT: string = process.env.PORT || "3001";

Add our main express app ./src/app.ts file:

import express from "express";
import bodyParser from "body-parser";
export const getApp = () => {
  const app = express();
  app.use(bodyParser.urlencoded({ extended: false }));
  app.use(bodyParser.json());

  app.get("/api/v1/test", (_, res) => {
    res.json({ ok: true });
  });
  return app;
};

Add our entry ./src/index.ts file:

import { PORT } from "src/configuration";
import { getApp } from "src/app";
const startServer = () => {
  try {
    const app = getApp();
    app.listen(PORT, () => {
      console.log(`server started at http://localhost:${PORT}`);
    });
  } catch (error) {
    console.error(error);
  }
};
startServer();

Add an example test at ./src/app.spec.ts file:

import { expect } from "chai";
import request from "supertest";
import { getApp } from "src/app";
describe("/api/v1/test", () => {
  it("works", async () => {
    const app = getApp();
    const res = await request(app).get("/api/v1/test");
    const { ok } = res.body;
    expect(res.status).to.equal(200);
    expect(ok).to.equal(true);
  });
});

Let's run tests:

npm run test

And we can start our dev server (It will automatically restart on file changes):

npm run dev

Check in your browser if our test endpoint works http://localhost:3001/api/v1/test

We can now build and run our server to check if everything compiled properly:

npm run build && npm start

Let me know if you have any problems or you think this “basic” setup should cover some more stuff as well.

Everything I just showed you is available here as a ready to clone repo:

https://github.com/michal-wrzosek/express-boilerplate