Node starter project (TypeScript + Docker)

Node starter project (TypeScript + Docker)

Project skeleton

We will assume that the project will have several smaller projects, ie. container, e.g. a part for microservices, a part for working with the database… It is good to create subfolders in the root of the project for each smaller project, e.g.:

Creating package.json

First we will create a package.json file in the root folder of the project. This file will contain all the necessary information about the project, such as name, version, dependencies… To create the basic version of package.json, we will use the npm init command with the flag “YES” “-y” to fill everything by itself without asking:

After that the package.json file will look like this:

For further work, we need to create and define the initial JS file. We will create the initial JS file in the “src” folder, which we will place in the “api” folder (eg app.js). After that, we need to modify the location and name of the initial file as part of package.json:

Typscript support

  1. TypeScript

    First we need to install TypeScript itself:

  2. tsconfig.json

    The

    tsconfig.json file is used to configure the TypeScript compiler (tsc). Used to define various compile options that control how TypeScript code will be translated to JavaScript. You can initialize this file with the command

    Example

  3. ts-node

    In order to run TypeScript directly from the Node environment without the need to compile it to JS beforehand, it is necessary to install the ts-node package:

    Next we create a simple “start” script to run the application in production:

    Invoking the command from the terminal starts the application:

  4. @types packages

    When you use JavaScript libraries in a TypeScript project, you need to install the appropriate types in order to debug at the compilation level. JavaScript is a dynamically typed language, which means that there are no built-in types, and in order for TypeScript to know which types should be used for Node.js (written in JS), we need a list of those definitions, which can be found in the package @types/node.

    If, for example, we’re using the “fs” module, without the @types/node package, the TypeScript compiler wouldn’t know anything about it, leading to compile-time errors. The same goes for Express, TypeScript doesn’t know what types it should use for variables written in Express, so we need to help it not throw compile errors by installing the package @types/express

  5. Nodemon

    The nodemon package allows us to automatically restart the application on the server when it detects changes in the files in the project, and is installed as follows:

Defining scripts

If we have the nodemon package installed, then we can create a script with which we will restart the server every time there is a code change. That script is usually called “dev” because it is used locally during development:

This script is called from the terminal as follows:.

After starting the script nodemon monitors the changes in the files inside the src directory and if it notices them, it automatically restarts the src/app.ts file using the ts-node library, compiling the TypeScript code “on the fly”.

NOTE:
For building JSproject, we can also create a script, e.g. "build": "tsc". This command will start the TypeScript compiler that will translate all .ts files into .js files according to the configuration defined in the tsconfig.json file and place them in the output directory (usually dist or build). The script is called as follows:.

ENV variables

In the node.js environment, as in other systems, environment variables gathered in one file are used, with the use of which we avoid hard-coded data scattered in the source code in different places, that file is marked with .env and we create it in the root folder of the project. So the structure of the project will look like this:

To load (environment) variables from the .env file into process.env, we need to install the dotenv package:

It is necessary at the beginning of code execution (eg in the initial file) to call the config() method from the DotEnv package because it loads the contents of the .env file and adds each variable to the “process.env” object. Read more about env variables in the article Environment Variables.

NOTE:
The .env file should be added to the .gitignore file to avoid sharing it with version controls, thus protecting sensitive information. Please note that in production, don’t forget to “manually” create a new .env file because it is not shipped with git !!!

Dockerization

Dockerfile

Dockerfile is a text file that contains instructions for creating a Docker image (eng. image). A Dockerfile contains a series of instructions that determine how a Docker image will be created.

Read more about Docker and its usage in the article “Docker: Running Apps Everywhere”

docker

Image creation (“build”)

The docker “build” command is used to create a Docker image from a Dockerfile. This command has several options that allow you to customize the build process. A detailed explanation of the options commonly used with the docker build command is as follows:

Where PATH is the directory containing the Dockerfile, URL can be a URL to a GIT repository, and “-“ allows the Dockerfile to be read from standard input. Here is a list of some options:

  • –tag abbreviated -t: Image tag. Allows image naming and version (tag) assignment.
  • –file abbreviated -f: Specifies the path to the Dockerfile if it is not in the current directory.
  • –build-arg: Pass variable as build argument. These variables can be used within the Dockerfile.
  • –no-cache: Disables the use of the build cache, ensuring that each layer is rebuilt.
  • –pull: Always pull the latest base image before building.
  • –label: Adds metadata (labels) to the image.

NOTE:
When Docker builds an image, it needs to have access to all files listed or used within the Dockerfile. These files are located in a directory called “context directory”. The dot (“.”) at the end of the docker build command simply means “use current directory as context directory”. This allows Docker to read the Dockerfile and any required resources from that directory to create the image.
Example:
The dot (.) in the docker build -f /path/to/Dockerfile .command indicates the current directory as the context directory. This means that Docker uses all files from that directory in the build process, while Dockerfile can be specified anywhere on the system using the -f option.

Example

This command will:

  • Create an image named my-image with tag (image version) 1.0
  • Use Dockerfile from specified path
  • Set build argument HTTP_PROXY
  • Perform build without using cache
  • Always download the latest base image
  • Add two labels to the image
  • The context directory is the current directory

Before starting the “build” command in Windows, we need to start the Docker desktop application, and it is preferable to be in the terminal in the root of the project where the Dockerfile is located.
After the executed command, you can check the image in the “Docker deskop” application, but you can also do the same through the terminal with the command:

Docker scripts

If we want to speed up work with Docker, we can prepare scripts in package.json like e.g.

Explained

  1. The docker:build script creates a Docker image from a Dockerfile, named “starter_node:latest”
  2. The script docker:run starts a new container, assigning it the name “node_api” based on the image “starter_node”
  3. The script docker:stop stops the container named “node_api”, then removes all stopped containers without asking for confirmation

For more about docker commands, see the article Docker: Running apps everywhere

SUMMARY

This is what the project structure looks like:

And this package.json file:

View the complete starter project code on GitHub https://github.com/choslee/node_typescript_doceker_starter