How to Develop with TypeScript in Docker with Hot Reloading


Developing with TypeScript inside Docker can be a bit tricky, but for the most part, it’s not too different from local TypeScript development.

By the end, we’ll have this following project structure.

📂 typescript-express-app
 ┗ 📂 src
    ┗ 📜 index.ts 
 ┣ 📜 .dockerignore
 ┣ 📜 Dockerfile
 ┣ 📜 nodemon.json
 ┣ 📜 package.json
 ┗ 📜 tsconfig.json

Install TypeScript Packages

We want to start by installing the relevant devDependencies.

We’ll be using ts-node, typescript, and nodemon. Be sure to install all the necessary @types/* packages.

npm install ts-node typescript nodemon --save-dev

Create tsconfig.json

Let’s create our tsconfig.json. We can add whatever options we’d like. The rootDir is the location of all our TypeScript files, which is /src in our case.

{
  "compilerOptions": {
    "target": "es6",
    "module": "commonjs",
    "rootDir": "./src",
    "outDir": "./build",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true
  }
}

Update package.json

We need to add a start script dev in our package.json that will be referenced in our Dockerfile.

Here, we’ll be running our src/index.ts to start our Express application using nodemon.

"scripts": {
  ...
  "dev": "nodemon -L src/index.ts",
  ...
}

The -L enforces Chokidar polling, which is needed to watch for file changes in our container.

Create nodemon.json

Let’s work on our second configuration file, nodemon.json. Our nodemon command above will follow the configurations we specify below.

{
  "watch": ["src"],
  "ext": "ts, json",
  "exec": "ts-node ./src/index.ts"
}

We’ll be watching for file changes inside our /src directory.

When a change occurs, we compile everything into JavaScript, and then run our src/index.ts again.

Create Dockerfile

Let’s create a Dockerfile, which will copy all of our source over to a container, and then run our dev script, which will trigger nodemon to watch and compile when neceesary.

FROM node:14.16.0-alpine3.10
WORKDIR /usr/app
COPY package*.json ./
RUN npm install
COPY . ./
CMD ["npm", "run", "dev"]

Mount the Source

We can start this container using docker build or with a docker-compose.yml.

I’ll demonstrate how to mount the source using Docker Compose.

We can mount the source using the volumes field, where we are indicating that the container’s /usr/app/src should point directly to the host machine’s ./src, allowing for nodemon inside the container to see the changes on our local, host machine.

version: "3"
services:
  typescript-express-app:
    build:
      context: .
      dockerfile: Dockerfile
    port: 
      - 5000:5000
    volumes:
      - ./src:/usr/app/src

Now, how can we turn all this into a production build?