Using TypeScript with Docker: A Guide to Containerize Your Applications

Using TypeScript with Docker: A Guide to Containerize Your Applications

Introduction

Prerequisites

Before we get into using Typescript and Docker together, it is important to understand what these technologies are on their own

  • Typescript is a programming language that extends JavaScript by adding new features such as type annotations, class and interface definitions, modules, decorators, and more. It's particularly useful for large-scale projects and for developing robust and maintainable code.

  • Docker is a platform that allows developers to create, deploy, and run applications in containers. Containers are lightweight, portable, and self-sufficient environments that can run on any machine that supports Docker, regardless of the underlying infrastructure, it provides several benefits, including portability, isolation, scalability, automation, and standardization

Understanding these two technologies makes it clear why knowing how to combine the two to build and deploy your applications can be beneficial

Benefits of using TypeScript and Docker

Using Typescript and Docker together gives the clear benefit of having robust and reliable apps but there are several more benefits when building and deploying applications:

  1. Type safety: TypeScript's type annotations can help catch errors early during development, making the code more predictable and easier to understand. This can reduce the number of bugs in the application.

  2. Improved development experience: TypeScript's advanced type system also comes with powerful autocomplete capabilities, providing developers with suggestions for properties, methods, and parameters, making coding faster and more efficient.

  3. Better maintainability: TypeScript's class and interface definitions, modules, and decorators can make the code more organized and reusable, making it easier to maintain and update the application over time.

  4. Consistency and reliability in deployment: Docker allows you to package your application and its dependencies into a single container, which can be easily deployed to any environment. This makes deployment more consistent and reliable and can help to reduce the number of environment-specific bugs.

  5. Scalability: Docker allows for easy scaling of applications by running multiple containers, which can handle increased traffic and workloads.

  6. Portability: Docker containers can be easily moved between different environments, such as development, staging, and production. This ensures consistency and reduces complexity.

  7. Isolation: Docker allows multiple applications or services to run on the same machine without interfering with each other, improving the security and stability of the application.

  8. Improved collaboration: Docker provides a consistent set of tools and technologies that can be used across different environments (So no more, "but it worked on my machine" from your peers), making it easier for developers to collaborate and share their work.

Setting up a TypeScript and Docker project

Let's take an example of a simple Nodejs application to get/create todos in typescript. (make sure you have Node, npm and Docker installed to follow along)

Step 1 - Setting up

To get started open your directory and setup your node app as usual with express.js, or follow the code given below

npm init -y
npm install express body-parser

Step 2- Adding TypeScript

To add Typescript run the following command

npm install --save-dev typescript @types/express nodemon ts-node

This will install typescript as a dev dependency along with types for express, and nodemon which we will use for testing the app locally along with ts-node

TypeScript uses a file called tsconfig.json to configure the compiler options for a project. Create a tsconfig.json file in the root of the project directory and paste the following JSON

{
  "compilerOptions": {
    "module": "commonjs",
    "esModuleInterop": true,
    "target": "es6",
    "moduleResolution": "node",
    "sourceMap": true,
    "outDir": "build"
  },
  "lib": ["es2015"]
}

At this point, your directory should look like this

--node_modules/ 
--package.json 
--tsconfig.json

Step 3 - Creating the App

Then create App.ts file ( you can name it anything you like I prefer App.ts) and paste the code below

import express, { Request, Response } from 'express';
import bodyParser from 'body-parser';

interface Todo {
    id: number;
    task: string;
    completed: boolean;
}

const app = express();
app.use(bodyParser.json());
let todos: Todo[] = [
    {id: 1, task: "Take out trash", completed: false},
    {id: 2, task: "Do laundry", completed: true}
];

app.get("/", (req: Request, res: Response) => {
    res.send("Hello World");
});


app.get('/todos', (req: Request, res: Response) => {
    res.send(todos);
});

app.post('/todos', (req: Request, res: Response) => {
    const newTodo: Todo = req.body;
    todos.push(newTodo);
    res.send(newTodo);
});

app.listen(3000, () => {
    console.log('Server is running on port 3000');
});

At this point, your directory should be looking like this

--node_modules/
--package.json
--tsconfig.json
--App.js

Step 4 -Time to test the app

To check if the app runs, we already have installed the tools need nodemon which uses ts-node to auto compile and autorun our app with changes, BUT we need to make some changes in our package.json

{
...,
"main": "App.ts",
...,
"scripts":{  
    "dev": "nodemon",
    "start": "node build/App.js",
    "build": "tsc --project ./"
  },
...
}

dev is used for testing, while the build is used to compile our typescript code and start to run the compiled javascript app.

To start the test server run the following command

npm run dev

Once nodemon finishes you should see the following

$ nodemon
[nodemon] 2.0.20
[nodemon] to restart at any time, enter `rs`
[nodemon] watching path(s): *.*
[nodemon] watching extensions: ts,json
[nodemon] starting `ts-node App.ts`
Server is running on port 3000

Step 5 - Setting up Docker

The next step is to set up Docker, we do that by creating a Dockerfile and paste the following:

FROM node:14-alpine
WORKDIR /app
COPY . .
RUN npm install
RUN npm run build
EXPOSE 3000
CMD ["npm","run", "start"]

This is the Dockerfile which is the schema to create your docker image,

the COPY command copies everything in the root directory to the app directory inside the docker image, to avoid us also copying the node modules we need to create a .dockerIgnore file and add the following inside it,

/node_modules

/build

Now to create the docker image run this command in your terminal:

docker build . -t tsdocker

This will create the docker image

docker run -p 3000:3000 tsdocker

You can open HTTP://Localhost:3000 and get the message Hello World on your screen and there you have it. You have set up your typescript app with docker. We will further how to deploy with a platform like render, in some future blog

Conclusion

Using TypeScript and Docker together can bring many benefits to a Node.js application. TypeScript provides a way to catch bugs early on by providing type checking and interfaces, while Docker allows for easy deployment and scaling of the application by packaging it in a lightweight container. Additionally, Docker provides a consistent environment regardless of the underlying infrastructure, which can prevent issues caused by differences between development and production environments. Furthermore, TypeScript and Docker make it simple to test the application in various environments as well as manage infrastructure and scaling. Finally, they can help to improve the overall development and deployment process by automating the building, testing, and deployment of code changes.

However, we haven't covered all the things, some advanced things you can cover on your own or through my future blogs are using multi-stage build in Dockerfile or using docker compose to run multiple containers.

Did you find this article valuable?

Support Yash Chavan's Blog by becoming a sponsor. Any amount is appreciated!