debugging node.js with typescript and docker

let's start with a blank directory first

yarn init -y
npx gitignore node
yarn add -D @types/node @types/express typescript
yarn add express

add tsconfig.json file to the root of the project

{
  "compilerOptions": {
    "target": "es5",
    "strict": true,
    "esModuleInterop": true,
    "composite": true,
    "baseUrl": ".",
    "paths":{
      "@lib1": ["./lib1"]
    }
  }
}
yarn add -D nodemon prettier ts-node tsconfig-paths

add .prettierrc

{}

add script to package.json

  "scripts": {
    "dev": "docker-compose -f docker-compose.yml up --build"
  },

add a few extras to package.json

  "nodemonConfig": {
    "watch": [
      "app1.ts",
      "app2.ts",
      "lib1.ts"
    ],
    "ext": "ts",
    "ignore": [
      ".git",
      "node_modules/**/node_modules"
    ],
    "execMap": {
      "ts": "node --require ts-node/register -r tsconfig-paths/register"
    }
  }

now create our app1.ts

import express from "express";

const app1 = express();

app1.get("*", (req, res) => {
  const span = `<span style="text-decoration: underline;">app1</span>`;
  const heading = `<h1>This is a ${span}</h1>`;
  res.send(heading);
});

app1.listen(3000, () => {
  console.info("app1 is listening on port 3000");
});

add docker-compose.yml

version: "3.8"
services:
  app1:
    image: node:16
    volumes:
      - ./node_modules:/node_modules
      - ./package.json:/package.json
      - ./tsconfig.json:/tsconfig.json
      - ./app1.ts:/app1.ts
    ports:
      - 3000:3000
    command: yarn ts-node app1.ts

to test everything out run

docker-compose up --build

this will download the node:16 image if you have not used it before and now you should see your new image. You could also run docker-compose up -d to run in detached mode. To stop, run docker-compose down and it will shut down your container.

now you can browse to localhost:3000 and you should see your shiny new app

now change up the docker-compose file

  ports:
    - 9229:9229
command: yarn nodemon --inspect=0.0.0.0:9229 app1.ts

now we will have hot-reloading on our server

for the vscode debugger, we will use this code

{
  // Use IntelliSense to learn about possible attributes.
  // Hover to view descriptions of existing attributes.
  // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
  "version": "0.2.0",
  "configurations": [
    {
      "name": "app1",
      "type": "node",
      "request": "attach",
      "port":9229,
      "restart":true,
      "localRoot":"${workspaceFolder}",
      "remoteRoot": "/"
    }
  ]
}

now if you run

docker-compose -f docker-compose.yml up --build

now you can launch the debugger, add breakpoints and you should be good to go

now let's create another app, app2.ts. it will look the same

import express from "express";

const app1 = express();

app1.get("*", (req, res) => {
  const span = `<span style="text-decoration: underline;">app2</span>`;
  const heading = `<h1>This is a ${span}</h1>`;
  res.send(heading);
});

app1.listen(3000, () => {
  console.info("app2 is listening on port 3000");
});

now update the docker-compose file

version: '3'
services:
  app1:
    image: node:14
    volumes:
      - ./node_modules:/node_modules
      - ./package.json:/package.json
      - ./tsconfig.json:/tsconfig.json
      - ./app1.ts:/app1.ts
      - ./lib1.ts:/lib1.ts
    ports:
      - 3000:3000
      - 9229:9229
    command: yarn nodemon --signal SIGINT --inspect=0.0.0.0:9229 --nolazy app1.ts

  app2:
    image: node:14
    volumes:
        - ./node_modules:/node_modules
        - ./package.json:/package.json
        - ./tsconfig.json:/tsconfig.json
        - ./app2.ts:/app2.ts
        - ./lib1.ts:/lib1.ts
    ports:
      - 3001:3000
      - 9230:9229
    command: yarn nodemon --signal SIGINT --inspect=0.0.0.0:9229 --nolazy app2.ts

now they should both be able to start up

now update our launch configuration

{
  // Use IntelliSense to learn about possible attributes.
  // Hover to view descriptions of existing attributes.
  // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
  "version": "0.2.0",
  "configurations": [
    {
      "name": "app1",
      "type": "node",
      "request": "attach",
      "port":9229,
      "restart":true,
      "localRoot":"${workspaceFolder}",
      "remoteRoot": "/"
    },
    {
      "name": "app2",
      "type": "node",
      "request": "attach",
      "port":9230,
      "restart":true,
      "localRoot":"${workspaceFolder}",
      "remoteRoot": "/"
    }
  ]
}