Debugging ExpressJS app with Docker and Visual Studio Code DevContainers
Tags: vscode, express, docker, newbie
Published: 2020-10-26 06:17:40

---
title: Debugging ExpressJS app with Docker and Visual Studio Code DevContainers
published: true
description: For a newbie, Steps to setup DevContainers or Remote Containers, with Visual Studio Code, for ExpressJS application, but will work for pretty much everything
tags: vscode, expressjs, docker, newbie
cover_image: https://cdn.hashnode.com/res/hashnode/image/upload/v1602587054672/gBRDY1bGw.jpeg
---

This article was originally published on my blog ( https://blog.mandraketech.in/vscode-devcontainers ) on Oct 13, 2020.

When I started my journey with NodeJS and ExpressJS, one of the things I saw in the trainings and blogs were instructions on installing NodeJS runtime. Although its trivial, but coming from the world of Enterprise application development where deployment to customer and being able to recreate the exact environment is critical, I wondered if there is an application of Docker in this workflow.

As I explored Docker, and Docker Compose, I saw that the starting point would have been easier if the instructions for using those with NodeJS and Express were available. But then, as I continued to explore, I did not find anything that could walk me through setting up VS Code, my editor of choice, for developing my application while still using breakpoints, as I would, with applications developed locally on my machine.

This is where the hunt to make something work, and make it easy for others to use, started. After exploring various options, I zoomed in on a workflow, that would get me started quickly. Like everything in life, there is some work required. So, let's begin.

Here are things we will do in this blog:

- Create an Express application
- Create a Docker and Docker-Compose environment
- Run and Debug in VS Code

The prerequisites for reading further are:
- Docker For Desktop installed ( https://www.docker.com/products/docker-desktop )
- Visual Studio Code installed
- NodeJS installed ( Yeah! I know. Only to generate the app, never again )
- An Internet connection

I am going to assume no understanding of Docker, and an going to skip all attempt at explaining how the steps work, in this post. The details on that will come as follow up, because it drive focus away from the intent of this article.

At the end of the steps, you will have a generated Express template, Dockerised, and a VS Code environment for debugging the application.

I am not getting into details of using Docker in production for this app, just development.

Supporting code, with follow along commit history, is available on  [Github](https://github.com/navneetkarnani/vscode-devcontainer-nodejs-sample)  

Generate ExpressJS app
===

The benefits of using Docker are to not need local installation of tools. Ironically, in the process I am about to demonstrate, the first step requires that NodeJS be installed. But this is only the first time. You wont need it after this, ever.

To generate the Express app, run the command:

```
npx express-generator 
``` 

You will see the output as this:

![Express Generator Output](https://cdn.hashnode.com/res/hashnode/image/upload/v1602587007178/sYdRwN2_Y.png)

You don't need to do anything to run `npm install` yet. This is why it was an irony that you need 

Create local Git Repo
===

As a matter of best practice, I suggest initialising a local git repository, just in case you need to roll back to this point. So, here are the steps:

```
git init .
git add .
git commit -m "new express app (generated)"
```

VS Code configuration
===

Start VS Code in the selected directory by running:
``` code . ```

Switch to the Extensions tab in VS Code, and ensure you have the following extensions installed (both published by Microsoft):
- Docker ( ms-azuretools.vscode-docker )
- Remote Containers ( ms-vscode-remote.remote-containers )

You must have a minimum of these in the VS Code environment, and really, after what we do today, you won't need any installed here either. You will discover why, as we progress.


![VS Code Extensions list](https://cdn.hashnode.com/res/hashnode/image/upload/v1602586613986/Uj_xeiSDA.png "VS Code Extentions List")

Let the VS Code extensions do their magic
===
- Start up the "Command Palette" in VS Code using Cmd+Shift+P (Ctrl+Shift+P on Windows) or from the "View" menu
- Invoke the action named "Docker: Add Docker Files to Workspace"
- Use the default values for the next few prompts:
-- Application Platform: Node JS
-- Port that the application listens on: 3000
-- Include optional Docker Compose files: Yes

The git tab on VSCode will tell you that 6 files have been created, ready to be added to git.

![VS Code Generate Docker workflow](https://cdn.hashnode.com/res/hashnode/image/upload/v1602590587650/2XiaSrvV2.gif "Steps to generate Docker files")

Again, commit the files, just in case you need to roll back here. So, use VS Code, add the files, and commit using the comment "Add generated Docker files"

Welcome to Remote Containers
===
As you notice, we have not yet run the magical command 
```npm install```

But hang on a little longer.

Let us generate the last bit of files, before we edit them. So, back to the "Command Palette", this time we want to run the command: "Remote-Containers: Add Development Container Configuration Files".

For the next prompt ```How would you like to create your container configuration?```  use the following value: ```From docker-compose.debug.yml```


![VSCode Generate DevContainer Config](https://cdn.hashnode.com/res/hashnode/image/upload/v1602591373900/M8Ebmy4qR.gif "Steps to generate the Dev Container config files")

You will see a prompt to reopen to develop in container, but not yet. Need some manual steps.

But before we go there, add the files to git, with the comment, "add generated devcontainer config"

Cleaning up
===
First step is to delete the `.vscode` directory and its contents. These files were generated by the Docker extension. We don't need them, and we want to keep focus on our task. If you need them, they are in the git history for you.

Delete, and commit.

Updating the Dev Dockerfile
===
Follow these steps:

- Create a copy of the `Dockerfile` and name it `Dockerfile.debug`
- Edit the `Dockerfile.debug` and do the following:
-- Remove the `-alpine` in the first line. We need a full environment for debugging
-- Change the line 
```RUN npm install --production --silent && mv node_modules ../```
to 
```RUN npm install```

The file will look something like this:
```
FROM node:12.18
ENV NODE_ENV production
WORKDIR /usr/src/app
COPY ["package.json", "package-lock.json*", "npm-shrinkwrap.json*", "./"]
RUN npm install
COPY . .
EXPOSE 3000
CMD ["npm", "start"]
```
You may change the version of Node in the `FROM` command, and make sure you do it in the `Dockerfile` too, so that you are consistent.

Again, not getting into the production usage of this app in this post.

Do not overwrite node_modules
===
The idea behind using `npm install` inside the container is to ensure the right module binaries are available for that environment. We do not want to use the node_modules for your local machine there. So, we need to update the `docker-compose.debug.yml` file.

I am posting the full line here. I have changes the `build` section, and added `volumes`. git will show you the lines that changed. YAML files are very sensitive to the indentation, so don't want you to spend hours figuring out why it doesn't work.

```
version: '3.4'

services:
  sample:
    image: sample
    build:
      context: .
      dockerfile: Dockerfile.debug
    environment:
      NODE_ENV: development
    ports:
      - 3000:3000
      - 9229:9229
    volumes: 
      - /usr/src/app/node_modules  
    command: ["node", "--inspect=0.0.0.0:9229", "./bin/www"]
```

Ensuring VS Code is looking at the right place
===
As you notice in the files we have worked with yet, specifically the `Dockerfile.debug` and `docker-compose.debug.yml`, there are specific references to the location of the source in the container. ( `/usr/src/app` ). So we need to make sure that VS Code will recognise that as the location to work with. Let's do that.

- Open the `.devcontainer/devcontainer.json` file
- Find `workspaceFolder` and change the value from `/workspace` to `/usr/src/app`

And, we also need to update the location that the local sources will be mounted on-to:

- Open the `.devcontainer/docker-compose.yml` file
- Look for the `volumes` section 
- The comment there clearly calls out: `Update this to wherever you want VS Code to mount the folder of your project`
- So, change `/workspace` to `/usr/src/app`

Here are the file changes:

![vscode-change-dev-files.gif](https://cdn.hashnode.com/res/hashnode/image/upload/v1602595061086/s_v9_UZ1K.gif "VS Code changed files")

As always, commit to git.

One more thing ...
===
No, I am not going to say those magic words. We are done. You can, at this time, click on the green icon at the bottom left of the VS Code window, or use the 'Command Palette' and "Reopen in Container". 

VS Code is helpful to show the logs, if you are interested, when you click the appropriate link in the message shown during the deployment.

You are now ready to go.

Ok, there is a one more thing. If you change the `package.json`, and hence need to update the `node_modules`, and, for some reason, it does't help, you can open the `Command Palette` and run "Rebuild and Reopen in Container".

Niceties
===
If there are extensions you like to use, in VS Code, those can be added to the "extensions" property in the `.devcontainer/devcontainer.json` file.

As you will know, you can open the package.json, and see the "Debug" prompt there to be able to debug your application, in-place.

Have fun !!!


About the Author:
Navneet Karnani is a Full Stack Ployglot veteran, and explores technology to build great products, always striving to be extract more productivity from the tools he uses.