My journey of learning back end (1): Setup

Sang Dang's photo
Sang Dang

Published on Sep 4, 2021

4 min read

Subscribe to my newsletter and never miss my upcoming articles

This is not a series of tutorials. So there are parts that you might have to work on your own, like, setup ESLint, installing/setup Node on your machine. Luckily you can do it with just a couple of Google searchs.

Reason? I want to learn more. I think it's a curse of being developer :D You always want to be better, know more, make things happens easier, work faster. You name it.

In my current company, Smartly, we work with Facebook's API heavily, and it's not the best API system in the world. And Smartly has a super complex system polling system to handle problems may occur from working with Facebook. I admire it and want to learn more from it. But the complexity of the whole system makes it hard to follow. So I want to re-create a mock version of it, using similar tools, to understand the architecture, how the data flow, etc.

That's why I chose the tech stack as close as possible. Koa.js, Bull, Docker, TypeScript.

First step is creating a working server with Koa. I learned a lot from this template .

This is code in server.ts. You can see the whole snapshot in this commit .

import Koa from 'koa';
import Router from 'koa-router';

const app = new Koa();
const router = new Router();

router.get('/', async (ctx) => {
  ctx.body = 'Hello World!';



console.log('Server running on port 3000');

Note: Some old tutorials might show you the router with /* and it will cause the error:

    throw new TypeError(`Unexpected ${nextType} at ${index}, expected ${type}`);
TypeError: Unexpected MODIFIER at 1, expected END

According to Koa team, it's a feature, not a bug :D

Screenshot 2021-09-04 at 12.43.10.png

OK, server can run, now we need to setup Docker. I will write more details in this section because this is the part I have to learn.

# Dockerfile contains specifications for creating an image


# This tells Docker to install an OS (Alpine) with Node
FROM node:${NODE_VERSION}-alpine

# EXPOSE is not publishing any ports, it is just a form of a documentation
EXPOSE 3000 9229

#  Creates a folder in the docker container called 'src'

# Because Alpine is so small that we have to install things we need
RUN apk add    postgresql-client redis

ARG environment

# required for docker:test:watch
RUN if [ "${environment}" = "development" ]; then apk add git; fi

# Copy the dependencies file into our container folder
ADD package.json yarn.lock ./

# This downloads and installs all the dependencies we need for our app
RUN yarn install --frozen-lockfile --link-duplicates

# Copy everything from our current directory to the WORKDIR. This moves all of our source code
ADD . .

RUN yarn build

CMD ["yarn", "start"]

The setup copied from this article .

Now, make the docker-compose.yml, I learned from this post.

I want to have a base yml file, and then one for development, and one for production, so I use the extends:


version: "3.8"

    image: redis:6.2-alpine
      test: "[ $$(redis-cli ping) == 'PONG' ]"
      interval: 5s
      - "24001:6379"
    image: postgres:13.4-alpine
      - "5432:5432"

docker-compose.local.yml: (dev)

version: "3.8"

      file: docker-compose.base.yml
      service: redis
      file: docker-compose.base.yml
      service: postgres
      - POSTGRES_DB=canvasser_development
      - POSTGRES_USER=canvasser
      context: .
        environment: "development"
      - .:/src
    working_dir: /src
      - POSTGRES_URL=postgres://canvasser@postgres:5432/canvasser_development
      - REDIS_URL=redis://redis:6379/14
      - postgres
      - redis


version: "3.8"

      file: docker-compose.base.yml
      service: redis
      file: docker-compose.base.yml
      service: postgres
    build: .
      - .:/src
      - "19000:19000"
      - PORT=19000

Now run docker-compose -f docker-compose.local.yml up and we will see postgres, redis, and our app will be up and running.

Next step we will start connect our Koa app into Postgres, and get Bull working.

Some notes:

  • ADD and COPY

    There are some differences between ADD and COPY, but in a nutshell, "the major difference is that ADD can do more than COPY:"
- ADD allows <src> to be a URL
- Referring to comments below, the ADD documentation states that:

If is a local tar archive in a recognized compression format (identity, gzip, bzip2 or xz) then it is unpacked as a directory. Resources from remote URLs are not decompressed.

  • Documentation is not correct

    This comment in GitHub confirms that we can use extends in version 3, but the documentation still not update (yet).

  • If you see the error:

    failed to create LLB definition: no build stage in current context

    Check this gist . In short, do not add anything before FROM in Dockerfile.

Share this