Dockerize Node.js TypeScript with Docker, MySQL and Compose

Posted January 25, 2024
Dockerize Node.js TypeScript with Docker, MySQL and Compose

Do you want to run your Node.js MySQL TypeScript apps with Docker and Docker Compose? This guide explains every step to getting a TypeScript Node.js app Dockerize and running as a complete Docker container.

Along this guide, you learn:

  • An example application set up using TypeScript, Node.js and MySQL.
  • How to create a Multi Build Dockerfile to Run TypeScript and Node.js on Docker.
  • The right way to connect Node.js and TypeScript on Docker with MySQL using Docker Compose.
  • How to access a MySQL container with Adminer.
  • How to populate your MySQL database with sample data and use it on your Node.js TypeScript API.

Now ensure you have Docker installed and running on your computer.

Setting up TypeScript Node.js App with Docker

In this example tutorial, we won’t create a Node.js API from scratch. I will assume you have a working Express and MySQL TypeScript app working. If not, check this TypeScript NodeJS Server tutorial with MySQL2 and ExpressJS

First, clone this GitHub repository to get a working TypeScript NodeJS Server with MySQL2 and ExpressJS:

git clone kimkimani/TypeScript-NodeJS-MySQL2

You should have the API ready:

Dockerize Node.js TypeScript with Docker, MySQL and Compose

Ready? Let’s now dive in and learn how to Dockerize your TypeScript Node.js app with MySQL using a Docker container.

Preparing TypeScript with Docker and Node.js

To be able to run TypeScript on Docker, you will need to make sure the following changes are ready:

To build TypeScript, Docker will use build as the outDir. This means you must have the following line in your tsconfig.json file:

/* Add inside compilerOptions */
"outDir": "./build",
"rootDir": "./src",

You will need commands to run your TypeScript. Docker will use the same commands. In your package.json file, ensure the following commands are ready:

  "scripts": {
    "build": "tsc",
    "dev": "nodemon --watch src --exec 'ts-node' src/index.ts",
    "prod": "node -r ts-node/register/transpile-only build/index.js"
  },

Dockerize Node.js TypeScript with Docker, MySQL and Compose

Creating Dockerfile to Run Node.js and TypeScript on Docker

A Dockerfile uses a script to create a Docker image. It contains instructions for building a Node.js app and how to run TypeScript code with Docker.

In your root directory (based on your APP OR the one you have cloned above), create a new file and name it Dockerfile. We will use Multi-stage to run Node.js and TypeScript with Docker.

A Multi-stage Dockerfile allows you to create different sets of instructions within the same Dockerfile and target TypeScript in development or production mode. This way:

  • You will use multiple FROM statements in a single Dockerfile
  • create multiple intermediate images during the build process.
  • Each stage is like a separate image in the build process.
  • You will copy artifacts from one stage to another, keep the final image size smaller, and only include what is necessary for runtime.
  • Have commands to run TypeScript in development and production mode on Docker

Now add the following instructions to this Dockerfile:

# .......Development Stage.......
FROM node:20-alpine as development
# Set the working directory in the container
WORKDIR /app
# Copy package.json and package-lock.json to the working directory
COPY package.json package-lock.json ./
# Upgrade npm to the latest version globally
RUN npm install -g npm@latest
# Install project dependencies
RUN npm install
# Install ts-node globally for running TypeScript code
RUN npm install -g ts-node
# Copy the entire application code into the container
COPY . .
# Build the application 
RUN npm run build

# .......Production Stage.......
FROM node:20-alpine as production
# Define an argument for the Node environment 
# with a default value of "production"
ARG NODE_ENV=production
# Set the environment variable for the Node environment
ENV NODE_ENV=${NODE_ENV}
# Set the working directory in the container
WORKDIR /app
# Copy package.json and package-lock.json to the working directory
COPY package.json package-lock.json ./
# Install only production dependencies
RUN npm install --only=production
# Copy the entire application code into the container
COPY . .
# Copy the build artifacts from the development stage to the production stage
COPY --from=development /app/build ./build
# Default command to run when the container starts in production mode
CMD npm run prod

In this example:

  • Docker will pull and use Node.js 20 on Alpine Linux as the base image.
  • It will copy your TypeScript code to Docker on app as the working directory.
  • This way, Docker will install dependencies and build your app as such.
  • Note that we have an ENV NODE_ENV=${NODE_ENV}.

The same Docker creates a multi-stage build for your TypeScript Node.js application based on development and production environments. In this case:

  • Stage 1 (development) will set up the working directory, install dependencies, copy the application code, and build the application. This way, Docker will compile TypeScript to JavaScript within Docker in the working
  • This first stage will allow you to run TypeScript and Node.js on development while on Docker. The stage builds and prepares the application.
  • Stage 2 (production) creates a production environment.
  • The --from=development command instructs Docker to copy the compiled TypeScript code, allowing a clean separation between development and production dependencies.

Creating TypeScript MySQL Database Scripts on Docker.

Up to now, you have the correct code needed to get Node.js and TypeScript running on Docker. Here, we are using MySQL as the database. If you are on Docker, you don’t need to manually create your database and table. We will use Docker Compose to do this.

Therefore, you will need SQL start scripts that MySQL will instruct Docker to use to initialize your database data. In your working directory, create a script folder and add:

  • Use the schema.sql file to create the database and the table. This example will use the blog as the database and posts as the table:
/* Create the database */
CREATE DATABASE IF NOT EXISTS blog;

/* Switch to the blog database */
USE blog;

/* Drop existing tables if they exist */
DROP TABLE IF EXISTS posts;

/* Create the tables */
CREATE TABLE posts(
    /* Add auto-incrementing primary key */
    id INT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,
    /* title column*/
    title VARCHAR(255) NOT NULL,
    /* description column of type TEXT */
    description TEXT NOT NULL,
    /* author column, allowing NULL values */
    author VARCHAR(255) DEFAULT NULL,
    /* content column */
    content VARCHAR(255) DEFAULT NULL,
    /* a created_at column with the TIMESTAMP data type
    set to the current timestamp by default */
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

Note that this SQL code must align with your application Model. If you use a different app, check your model/interface class and make sure the SQL code aligns with your structure.

  • data.sql contains sample data that MySQL will tell Docker to insert in your database before running TypeScript code:
INSERT INTO posts (title, description, author, content)
VALUES
    ('Introduction to SQL', 'Learn the basics of SQL', 'John Doe', 'SQL is a powerful language for managing relational databases.'),
    ('Web Development Tips', 'Tips for building modern web applications', 'Jane Smith', 'Stay updated with the latest web development trends and best practices.'),
    ('Data Science with Python', 'Explore the world of data science using Python', 'Robert Johnson', 'Python is widely used in the field of data science for its versatility and rich ecosystem.'),
    ('Delicious Recipes', 'Discover new and tasty recipes', 'Alice Brown', 'Try these mouthwatering recipes at home and impress your friends and family.'),
    ('Travel Adventures', 'Explore the wonders of the world', 'Michael Wilson', 'Embark on exciting travel adventures and create lasting memories.'),
    ('Fitness Journey', 'Tips for a healthy and active lifestyle', 'Emily Davis', 'Stay fit and healthy with these workout routines and nutrition advice.');

Spinning up Node.js, TypeScript and MySQL with Docker Compose

Let’s now dive into Docker Compose and get these Docker Containers ready. Docker compose tells Docker to run a multi-container setup. Here you will have:

  • A Node.js Docker container to run TypeScript
  • MySQL service will get MySQL up and running on Docker.
  • An Adminer container to access MySQL on a UI.

You only need one file to set up these three, a docker-compose.yml. Docker Compose will use it to manage and deploy them as a multi-container Docker app and as a single unit.

In your Working directory, create a docker-compose.yml file with the following Docker Compose instructions:

version: '3.8'
services:
  mysql_server:
    image: mysql:8.1.0
    environment:
      MYSQL_ROOT_PASSWORD: root
      MYSQL_DATABASE: blog
      MYSQL_USER: db-user
      MYSQL_PASSWORD: testpass
    ports:
      - 3306:3306
    volumes:
      - mysql-data:/var/lib/mysql
      - "./script/schema.sql:/docker-entrypoint-initdb.d/1.sql"
      - "./script/data.sql:/docker-entrypoint-initdb.d/2.sql"
    networks:
      - app-network

  adminer:
    image: adminer
    ports:
      - 8080:8080
    environment:
      ADMINER_DEFAULT_SERVER: mysql_server
    depends_on:
      - mysql_server
    networks:
      - app-network
  
  app:
    build:
      context: .
      dockerfile: Dockerfile
    volumes:
      - ./:/app
    environment:
      NODE_ENV: development
    depends_on:
      - mysql_server
    command: npm run dev
    ports:
      - "3000:3000"
    networks:
      - app-network
  
volumes:
  mysql-data:
networks:
  adminer-network:

Let’s break down each service.

  • mysql_server will run the MySQL server. In this case, it will expose 3306 and create a sample database, password, and username. At the same time, This service will execute the data.sql and schema.sql files to create your table and insert data.
  • app will execute the Dockerfile and set Node.js and TypeScript with Docker. In this section, you will target the development stage and run the command npm run dev to start the server. Also, note the ./:/app volume. I will show you its magic later.
  • To access MySQL, you will use adminer. You can check this Easy Apache Guide with Docker Compose, MySQL, and PhpMyAdmin guide if you want to use PhpMyAdmin instead.

Connecting MySQL and Node.js TypeScript DB connection from Docker

Now, you need to edit your TypeScript Node.js code and reflect its connection to the above MySQL variables. In this example, you will go to database.ts and add the following changes:

import { createPool } from 'mysql2/promise';

export async function connect() {
    const connection = await createPool({
        host: 'mysql_server',
        user: 'db-user',
        password: 'testpass',
        database: 'blog',
        connectionLimit: 10
    });

    return connection;
}

Note that TypeScript will use these MySQL connection variables based on how you have defined MySQL service with Docker in your docker-compose.yml file.

The Key point here is the host. It should not be localhost. The host name here is mysql_server, which is the Docker Compose service responsible for running MySQL.

If you are accessing these elements using a .env file or dotenv as follows:

import { createPool } from 'mysql2/promise';

export async function connect() {
    const connection = await createPool({
        host: process.env.MYSQL_HOST,
        user: process.env.MYSQL_USER,
        password: process.env.MYSQL_PASSWORD,
        database: process.env.MYSQL_DATABASE,
        port: Number(process.env.MYSQL_PORT),
        connectionLimit: Number(process.env.MYSQL_CONNECTION_LIMIT)
    });

    return connection;
}

You only need to include these variables in your docker-compose.yml file. However, you must add them to the app service in the environment section as follows:

  app:
    build:
      context: .
      dockerfile: Dockerfile
    volumes:
      - ./:/app
    environment:
      NODE_ENV: development
      MYSQL_HOST: mysql_server
      MYSQL_DATABASE: blog
      MYSQL_USER: db-user
      MYSQL_PASSWORD: testpass
      MYSQL_PORT: 3306
      MYSQL_CONNECTION_LIMIT: 10
    depends_on:
      - mysql_server
    command: npm run dev
    ports:
      - "3000:3000"
    networks:
      - app-network

Running the TypeScript Node.js Container

Every setup should be ready. Use the following command to spin up the containers.

docker-compose up --build -d

This command will build the TypeScript Node.js and MySQL container and run them on Docker:

Dockerize Node.js TypeScript with Docker, MySQL and Compose

Open Docker and ensure these services are up and running as follows:

Dockerize Node.js TypeScript with Docker, MySQL and Compose

To confirm the TypeScript code is running on Docker, click the app service:

Dockerize Node.js TypeScript with Docker, MySQL and Compose

Now, you will access these logs and know if Nodemon is running the TypeScript (on Docker) as expected:

Dockerize Node.js TypeScript with Docker, MySQL and Compose

Remember the - ./:/app volume you created in your Docker Compose file? Here is magic. Now if you change the code on your local machine, Docker will be able to pick up the changes and use Nodemon to rerun the new code. Note that this time you don’t have to rerun the docker-compose up --build -d command to get these TypeScript changes on Docker. Once you save the changes, Nodemon and Docker will work the magic:

Dockerize Node.js TypeScript with Docker, MySQL and Compose

Testing the Dockerize Typescript Node.js App

You have successfully built a TypeScript Node.js container. To test it, open Postman and send a GET request. This should fetch the first data you added using the data.sql file.

Send a GET request to http://localhost:3000/posts:

Dockerize Node.js TypeScript with Docker, MySQL and Compose

To add data, Use the POST request as follows:

Dockerize Node.js TypeScript with Docker, MySQL and Compose

Go further and test DELETE AND PUT.

Accessing MySQL with Adminer

We have Adminer running on http://localhost:8080/. Open it on the browser:

Dockerize Node.js TypeScript with Docker, MySQL and Compose

Here:

  • Select the MySQL system.
  • The server should be a mysql_server service.
  • Add username db-user
  • Add the database user password testpass
  • Use the blog database.

Once you log in, you will have the posts table, just as Docker created it:

Dockerize Node.js TypeScript with Docker, MySQL and Compose

Open this table and see your data right away:

Dockerize Node.js TypeScript with Docker, MySQL and Compose

Conclusion

This guide taught you how to create a Multi Build Dockerfile to Run TypeScript and Node.js on Docker. You now have the right way to connect Node.js and TypeScript on Docker with MySQL using Docker Compose. At the same time, access a MySQL container with Adminer. Check all the TypeScript apps and Docker codes on this GitHub repo.

Dockerize Node.js TypeScript with Docker, MySQL and Compose

Written By:

Joseph Chege