Replacing Docker with VSCode Tasks

Recently, I've worked on a project that used multiple microservice-like repositories e.g., app-frontend, app-backend, app-service-1, app-service-2.

I always use a "master" project that contains all the repositories as submodules. Then I create a docker-compose file to allow starting all services with a simple docker compose up. This has, so far, worked perfectly. But, I never quite liked that all of the output was in a single terminal, jumbled mess of all running microservices.

So I thought why not use VSCode's Tasks for this. It turned out it works pretty well. Using a simple dev all task, I can start all services in the correct order (this includes TS watch for live reload). And best of all, the logs are seperated for each microservice.

Workspace setup

To get started with this add all seperate repostiories to a workspace.

Save your workspace file and Open Workspace settings (JSON). You'll probably see something like this:

{
	"folders": [
		{
			"path": "D:\\projects\\project\\project-backend"
		},
		{
			"path": "D:\\projects\\project\\project-frontend"
		}
	],
	"settings": {}
}

Here I added two projects named project-backend, project-frontend. These are cloned repositories on my PC. Now they're empty, but let's pretend each one includes a TypeScript NodeJS project that has a npm start command.

Tasks

Since we have two projects, each one will need two tasks. One for tsc-watch and another for npm start. On top of a main helper task that will start all of the relevant tasks.

This is the boilerplate I usually write:

{
	"label": "Watch frontend",
    "type": "shell",
    "args": ["-w", "-p", "."],
    "options": {
    	"cwd": "..\\project-frontend"
    },
    "isBackground": true,
    "command": "tsc",
    "problemMatcher": "$tsc-watch"
},
{
    "label": "Dev frontend",
    "type": "shell",
    "options": {
    	"cwd": "..\\project-frontend"
    },
    "dependsOn": ["Watch frontend"],
    "command": "npm start",
    "problemMatcher": []
},

If you're not using TypeScript (or hot-reload for that matter) the Watch task isn't really needed.

Each batch of tasks uses dependsOn to start the watch task before running the app. For example, Dev frontend depends on Watch frontend, only after TS has recompiled will the app start with npm start.

The final task acts as a helper task, to avoid manually starting lots of tasks everytime:

{
	"label": "Dev all",
	"dependsOn": ["Dev frontend", "Dev backend"],
	"problemMatcher": []
}

Done

That's all that you really need. Just starting the Dev all task will now automatically also start tsc-watch for all projects as well as run npm start on each one.

Here's a full workspace example: https://pastebin.com/sVsdtSHr.