Building a Production - Ready Node.js App with TypeScript and Docker

In this article, we will see how to build a Node.js, TypeScript Application and deploy it to server with Docker. Building a Production - Ready Node.js App with TypeScript and Docker.

If you are new to typescript, watch this tutorial which covers the basics of TypeScript.

Application Setup

Firstly, you need to install typescript in your machine. Run the following command, this will install typescript globally in your machine.

1npm install -g typescript

Create a directory and initialize the node.js application with the command.

1npm init --yes

After that, you need to create a configuration file for typescript which compiles the typescript into javascript.

1tsc --init

it will create a configuration file called tsconfig.json which contains the TypeScript configuration for the application.

Configuring TypeScript

Further, the configuration file contains the compiler options which can be configured. important options are,

  • target - it can be either ES6 or ES5, based on the options, TypeScript compiles the code either to ES6 or ES5.
  • outDir - it specifies the directory where the compiled code will be stored.
  • rootDir - it specifies directory on which the typescript code will be.
  • moduleResolution - specifies the module resolution strategy

config ts

Once, it is configured. we need to install few dependencies to setup and run the typescript on express application.

Install the following dependencies using the command

1npm i -D typescript ts-node @types/node @types/express
  • ts-node - it is a package for using TypeScript with Node.js. we can run the application using ts-node app.ts
  • @types/node - it defines the custom types for Node.js in typescript
  • @types/express - it defines the custom types for express application in typescript

After that, create a scripts in package.json to compile and run the application.

1"scripts": {
2 "dev": "ts-node src/app.ts",
3 "start": "ts-node dist/app.js",
4 "build": "tsc -p ."
5 }

Building Express Application with TypeScript

Once TypeScript configuration is done, we can build the Express application using TypeScript.

Create a directory called src which contains the TypeScript files and add the app.ts file in that.

1import express, { Application, Request, Response, NextFunction } from "express"
2import bodyParser from "body-parser"
3
4const app: Application = express()
5
6app.use(bodyParser.json())
7app.use(bodyParser.urlencoded({ extended: true }))
8
9app.get("/", (req: Request, res: Response) => {
10 res.send("TS App is Running")
11})
12
13const PORT = process.env.PORT
14
15app.listen(PORT, () => {
16 console.log(`server is running on PORT ${PORT}`)
17})

one of the advantages of using TypeScript is defining the Type for the variable(Static Checking).

Here Express instance will be of type Application, Therefore, variable must be of type Application. same goes for Request,Response and Next Function(Middleware).

After that, we need to write a logic to connect the database. create a file called connect.ts and add the following code,

1import mongoose from "mongoose"
2
3type DBInput = {
4 db: string,
5}
6
7export default ({ db }: DBInput) => {
8 const connect = () => {
9 mongoose
10 .connect(db, { useNewUrlParser: true })
11 .then(() => {
12 return console.info(`Successfully connected to ${db}`)
13 })
14 .catch(err => {
15 console.error(`Error connecting to database :`, err)
16
17 return process.exit(1)
18 })
19 }
20
21 connect()
22
23 mongoose.connection.on("disconnected", connect)
24}

DBInput is a type which takes variable db as a string. we use it connect with mongodb.

After that, create directories Controllers,Models ,Routes and types in the root directory.

  • Controllers - contains all the business logic for the application
  • Models - contains all the Database Schema of Mongoose.
  • Routes - will have all the API Routes for the application
  • Types - will contains Custom types used in the Application

create a file User.mode.ts in Models Directory and add the following code,

1import mongoose, { Schema, Document } from "mongoose"
2
3export interface IUser extends Document {
4 email: String;
5 firstName: String;
6 lastName: String;
7}
8
9const UserSchema: Schema = new Schema({
10 email: {
11 type: String,
12 required: true,
13 unique: true,
14 },
15 firstName: {
16 type: String,
17 required: true,
18 },
19 lastName: {
20 type: String,
21 required: true,
22 },
23})
24
25export default mongoose.model < IUser > ("User", UserSchema)

Firstly, we define mongoose schema for user model and User Interface

In Controllers Directory, Create User.controller.ts file and add the following code

1import User,{ IUser } from '../Models/User.model';
2
3interface ICreateUserInput {
4 email: IUser['email'];
5 firstName: IUser['firstName'];
6 lastName: IUser['lastName'];
7}
8
9async function CreateUser({
10 email,
11 firstName,
12 lastName
13 }: ICreateUserInput): Promise<IUser> {
14 return User.create({
15 email,
16 firstName,
17 lastName
18 })
19 .then((data: IUser) => {
20 return data;
21 })
22 .catch((error: Error) => {
23 throw error;
24 });
25 }
26
27 export default {
28 CreateUser
29 };

After that, create a file index.ts in Routes directory and add the following code,

1import { RoutesInput } from "../types/route"
2import UserController from "../Controllers/User.controller"
3
4export default ({ app }: RoutesInput) => {
5 app.post("api/user", async (req, res) => {
6 const user = await UserController.CreateUser({
7 firstName: req.body.firstName,
8 lastName: req.body.lastName,
9 email: req.body.email,
10 })
11
12 return res.send({ user })
13 })
14}

RoutesInput is a custom type which defines the Express Application Type.

create a file types.ts in types directory and add the code,

1import { Application } from "express"
2export type RoutesInput = {
3 app: Application,
4}

update the app.ts with mongodb connection and routes of the application.

1import express, { Application, Request, Response, NextFunction } from "express"
2import "dotenv/config"
3import bodyParser from "body-parser"
4import Routes from "./Routes"
5import Connect from "./connect"
6
7const app: Application = express()
8
9app.use(bodyParser.json())
10app.use(bodyParser.urlencoded({ extended: true }))
11
12app.get("/", (req: Request, res: Response) => {
13 res.send("TS App is Running")
14})
15
16const PORT = process.env.PORT
17const db = "mongodb://localhost:27017/test"
18
19Connect({ db })
20Routes({ app })
21
22app.listen(PORT, () => {
23 console.log(`server is running on PORT ${PORT}`)
24})

To test the application, run the script npm run dev and visit the url http://localhost:4000

Docker Configuration

If you are new to docker, read about docker for node.js and docker configuration.

create a file Dockerfile in the root directory and add the following code.

1FROM node:10
2
3WORKDIR /usr/src/app
4
5COPY package.json ./
6
7RUN npm install
8
9RUN npm install pm2 -g
10
11RUN npm run build
12
13COPY ./dist .
14
15EXPOSE 4000
16
17CMD ["pm2-runtime","app.js"]

Basically, we take the node base image and install all our dependency in the docker image container

After that, we install process manager called pm2 which is used mostly in all production applications. then we copy the compiled code from local to docker image.

Further, create a file docker-compose.yml in the root directory and add the following code.

1version: "3"
2
3services:
4 app:
5 container_name: app
6 restart: always
7 build: .
8 environment:
9 - PORT=4000
10 ports:
11 - "4000:4000"
12 links:
13 - mongo
14 mongo:
15 container_name: mongo
16 image: mongo
17 ports:
18 - "27017:27017"

Docker compose combine multiple docker services and run it in a single container. Here, we combine MongoDB and Application images and run it container.

Docker Deployment and Running

Once, Dockerfile is added. run the following command,

1docker-compose up

it will deploy the compiled code in docker image and run it in the container.

Complete Source code contains Building a Production - Ready Node.js App with TypeScript and Docker.

Author

Hey, I’m Ganesh, Full stack engineer.I love to write technical content and help developers like me to grow in the industry. please consider supporting me.

To Read More

Building a Production-grade Nodejs,...

This article is the first part of building a production grade nodejs,graphql and...

Modern React Redux Tutorials with R...

This tutorial explain how you can build an application using modern react redux ...

Building a Piano with React Hooks

In this article, we will see how to build a piano with react hooks. Building a P...

TypeScript Basics - The Definitive ...

In this article, we will learn some basics of typescript which helps you to deve...

Here's why podman is more secured t...

In this article we will see about podman and why it is more secured way to run c...

Building a Production - Ready Node....

In this article, we will see how to build a Nodejs, TypeScript Application and d...

Nginx for Front-end Developers

This article is to explain Nginx for Front-end Developers in a much simpler way....

What is gRPC ? How to implement gRP...

Everyone talks about gRPC. Have you ever wonder how it works or how to implement...