Simple NodeJS Project with TypeScript and Kubernetes / Part 1

Sina Ahamadpour
6 min readApr 17, 2023

--

I was overthinking about publishing this or not, that part of my perfectionism brain kept disallowing me to do it because there are some parts in this series of articles that are not 100% correct in large-scale applications but maybe this would be a good starting point for new developers who are trying to figure out how to wrap up a really small application from A to Z.

Disclaimer

This series will give you a clue to start, so please be curious and try to read other references too. Try to make your hands dirty by coding and understanding the building blocks.

In this series you will see that I’ll create a really simple application with below attributes:

  • A note app is written in NodeJS by using Typescript
  • Clean-architecture applied
  • Testable
  • Dockerized
  • Contains K8s YAML files
  • With Postman collections (Env variables)

Setup your environment

I’ll use the below versions of the bundles:

  • npm 8.5.5
  • node v16.15.0

You can check yours by below commands

node --version
npm --version

After you made sure that you have the bundles then we can start by creating the package.json file by the following command:

npm init -y

This will create a package.json file for you and contains your installed packages' versions.

Next, we need ts-node and typescript to empower our stack with beloved TS features!

npm install ts-node typescript --save-dev
  • With ts-node, we can run our TS code and see the result on the fly.
  • With tsc, we compile the code and then we can run the pure JS output file by node

Now you can setup how your TS-compiler works by the following command:

./node_modules/.bin/tsc --init

This command will create a tsconfig.json file in your directory. That file holds all the necessary configs for our TS project and the way it has to compile code.

Make sure you have the right values too:

{
"compilerOptions": {
...
"target": "es2016",
...
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
...
"module": "commonjs",
"rootDir": "./src",
...
"outDir": "./build",
...
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
...
"strict": true,
...
"skipLibCheck": true
},
"include": [
"src/*"
],
"exclude": [
"node_modules",
"./node_modules",
"./node_modules/*",
"./node_modules/@types/node/index.d.ts",
]
}

Also, remember to create the following directories by running the below command:

mkdir src build

The src will hold our *.ts files as source code and build hold compiled *.js files.

We need an entry for our application so we will create a file in src as below:

touch src/app.ts

In that file, I just created a simple code to ensure our setup works as expected

src/app.ts

const greet = (name: string) => console.log(`Hello ${name}`)

greet('You!')

This is just a simple function that accepts a name as a string and sends that to the standard output, in this case, your terminal.

You can run that by this command:

./node_modules/.bin/ts-node src/app.ts

# OUTPUT:
# Hello You!

Also, you can build and run with Nodejs

./node_modules/.bin/tsc src/app.ts 

# This command does not have any output but it does build the JS file
# You can check the file by the below command
cat ./src/app.js

# OUTPUT:
# var greet = function (name) { return console.log("Hello ".concat(name)); };
# greet('You!');

If you look closely you noticed that it converted the function to the vanilla javascript and it removed the types, so it worked!

You can now run with pure NodeJS (v8 engine):

node ./src/app.js 

# OUTPUT:
# Hello You!

The last command creates a file that we don’t want to be saved in the src folder. so please remove that last file because we are going to do a better way

unlink ./src/app.js

Let’s update our package.json file as below:

{
"name": "noteapp-2",
"version": "1.0.0",
"main": "index.js",
"scripts": {
"start": "ts-node ./src/app",
"build": "tsc"
},
"keywords": [],
"author": "",
"license": "ISC",
"description": "",
"devDependencies": {
"ts-node": "^10.9.1",
"typescript": "^5.0.4"
}
}

Now you can run your code by the below commands:

npm run start

# You can eliminate the run only for "start" command
npm start

or build by the below command:

npm run build

node build/app.js

Let’s introduce Nodemon!

This beloved package will help you to develop easily. In my mind building and running every time we change the code is exhausting, so let’s adopt a better way to develop.

npm install nodemon --save-dev

This will save the nodemon package as a dev dependency.

Also, update the scripts part of the package.json as below:

{
...
"scripts": {
...
"dev": "nodemon ./src/app.ts",
...
},
...
}

Now by running the below command you can see the results by changing and saving the src/app.ts file

npm run dev

Voila! Now we have a simple scaffolded app structure to continue our journey through TS

Before finishing this article I wanted to add a really good package to make sure we write clean and easy-to-read code.

ESLint

ESLint helps you to follow a set of conventions or rules. No matter if you are a one-person army or a part of a big tech team, in both cases clean code matters!

Let’s install the package by the following command:

npm install --save-dev \
typescript@\* \
eslint@^8.0.1 \
eslint-plugin-promise@^6.0.0 \
eslint-plugin-import@^2.25.2 \
eslint-plugin-n@^15.0.0 \
@typescript-eslint/eslint-plugin@^5.43.0 \
eslint-config-standard-with-typescript@latest

Now you can initialize the configuration files automatically:

./node_modules/.bin/eslint -- --init

Make sure you have updated as below:

.eslintrc.json

{
"env": {
"browser": true,
"commonjs": true,
"es2021": true
},
"extends": "standard-with-typescript",
"overrides": [
],
"parserOptions": {
"ecmaVersion": "latest",
"project": "./tsconfig.json"
},
"rules" : {
"no-console": "off",
"indent": [ "error", 2 ],
"quotes": [ "error", "single" ],
"semi": [ "error", "never" ],
"no-trailing-spaces": [ "error", { "skipBlankLines": true } ],
"no-useless-constructor": [ "off" ],
"no-unused-vars": [ "error" ],
"arrow-spacing": [ "error" ],
"semi-spacing": [ "error" ],
"space-infix-ops": [ "error" ],
"space-before-blocks": [ "error" ],
"comma-spacing": [ "error" ],
"space-in-parens": [ "error" ],
"key-spacing": [ "error" ]
}
}

.eslintignore

node_modules
build
test
coverage
documentation
jest.config.ts

Let’s update the scripts part of the package.json file one more time!

{
...
"scripts": {
"start": "ts-node ./src/app",
"dev": "nodemon ./src/app.ts",
"build": "npm run lint && tsc",
"lint": "eslint --fix ."
},
...
}

How to test? Let’s mess up with our ./src/app.ts file as below:

const greet = (name: string)          =>        { console.log(`Hello ${name}`) }




greet( "Medium!" )

After running the npm run lint command it will fix the code as below:

const greet = (name: string) => { console.log(`Hello ${name}`) }

greet('Medium!')

It seems ESLint is unhappy with our code. let’s change and run again.

./src/app.ts

const greet = (name: string): void => { console.log(`Hello ${name}`) }

greet('Medium!')

It removed the extra spaces and converted double quotes to single quotes.

You can set up your favorite IDE to achieve the same result on saving the file. (I’ll skip that)

Congratulation! You came this far and made me happy by reading this article. It motivates me so much for the next part. I’ll promise to write ASAP.

Kind regards

--

--