Hire a Gang using Nodejs, Express, Typescript

Alireza

This is an instruction to create a project using Nodejs, Express and TypeScript which has live editing feature.

Nodejs + Expressjs + TypeScript

Introduction

First thing first, Why TypeScript?
Well, TypeScript in nutshell is scripting language which understands the typed structure and the final result is transpiled to Javascript, Although TypeScript itself is a subset of Javascript so every Javascript code is TypeScript too. TypeScript is an open-source programming language developed and maintained by Microsoft. So if we write code in TypeScript and the final result trasnpiled to Javascript it means it will run everywhere Javascript is resisting, Client side or Server side.

For the simple definition, TypeScript is Javascript with some added features which enables type checking. The main purpose of Typescript is usage in large-scale projects although my own experience tells me it is handy even in small projects. It almost removes all headaches on runtime.

This article will help you to scaffold a REST-API project on the server side using Nodejs, Express in Typescript.

One important need when you start debugging your code is the ability to live edit and watch the result as soon as possible, so this scaffold contains setup parts for making your code live editable.


Setup Node

First, we need to setup Nodejs, This is highly recommended to use nvm. nvm is Nodejs Version Manager, which enables your code to run against the different version of Node. It also removes your headaches on installing node on different OS platforms.

Install nvm

curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.11/install.sh | bash

Check nvm is installed successfully

nvm --version
nvm — version

List remote LTS versions

nvm ls-remote --lts

This command will scan remote repository and return the list of LTS versions on the repository.

Install both v10.13.0 and v8.4.0

nvm install --lts=Dubnium
nvm install v8.4.0

Switch between different downloaded versions

nvm use v10.13.0

Create our project folder and init the project using npm

# create our project folder
mkdir project
cd project
# using npm init command to make package.json and initial project
npm init

Create project structure

# source folder which contains our typescript files, 
# everything we write in typescript goes here
mkdir src
# build folder which contains final result
# and transpiled typescript into javascript
mkdir dist

At this point, our project looks like below but don’t worry it will get more detail and start working very soon.


Setup Typescript

I prefer to install TypeScript modules as dev dependencies locally and not globally, this gives me an option to switch and run my code with different Node versions and also run on different workspaces just with npm install command.

Install TypeScript tsc compiler

npm install typescript --save -D

By running above code the TypeScript compiler will install, now we need to add tsconfig.json which tells tsc how to deal with the project.

{
"compilerOptions": {
"target": "es6",
"module": "commonjs",
"lib": ["es2015", "es2016", "dom", "es2017", "es6", "es5"],
"esModuleInterop": true,
"noImplicitAny": true,
"moduleResolution": "node",
"sourceMap": false,
"outDir": "dist",
"baseUrl": ".",
"paths": {
"*": ["node_modules/*","src/types/*"]
}
},
"include": [
"src/**/*"
]
}

some important props in this config are:

  • target
    You need to define which version of Javascript the compiler should transpile into. It can be “es5”, “es6”, “es2015”, “es2016”, “es2017” or “esnext”.
  • module
    Specify module code generation: "None", "CommonJS", "AMD", "System", "UMD", "ES6", "ES2015" or "ESNext". For detail, I recommend reading this post for getting know difference between moduleswsss.
  • lib
    With lib you can specify a list of built-in API declaration groups that you can chose to include in your project. For instance, if you expect your runtime to have support for Map, Set and Promise (e.g. most evergreen browsers today), just include — lib es2015.collection,es2015.promise. Similarly you can exclude declarations you do not want to include in your project, e.g. DOM if you are working on a node project using — lib es5,es6.
    read this post.

For other config’s params please refer to TypeScript documentation

Add TS-Lint

An extensible linter for the TypeScript language.

npm install tslint tslint-config-prettier --save -D

tslint-config-prettier :
Do you want to use tslint and prettier without conflicts? tslint-config-prettier disables all conflicting rules that may cause such problems. Prettier takes care of the formatting whereas tslint takes care of all the other things.

Config TS-Lint:
Now add tslint.json to root of the project with the configuration as below:

{
"extends": ["tslint:recommended","tslint-config-prettier"],
"linterOptions": {
"exclude": ["config/**/*.js","node_modules/**/*.ts",
"coverage/lcov-report/*.js"]
},
"rules": {
"ordered-imports": false,
"object-literal-sort-keys" : false,
"no-console" : false,
}
}

Our project now seems like below:

typescript + nodejs scaffold project

Typings
TypeScript mainly adds type definition to Javascript, so if you are using Javascript libraries as a dependency in your project in order to prevent buggy usage of those libraries you need to add type definition to those libraries. Fortunately for a lot of community libraries which are most downloaded by developers, some people wrote the type definition. you can get those types by npm with prefix @types like @types/express.
There is a public place for these types which is The repository for high-quality TypeScript type definitions.


Setup Express

As the documentation says “Express is a minimal and flexible Node.js web application framework that provides a robust set of features for web and mobile applications.

# install external Javascript libraries
npm install express cors request body-parser --save
# install typings for those Javascript libraries
npm install @types/express @types/cors @types/request @types/body-parser --save -D

Server.ts
Our web application has a start point to execute and that is server.ts file.

Create file at “./src/server.ts” and write below code into:

// this will load app which contains our main structure and logic
import app from “./app”;
// use this line to get port from environment variable
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
# if you put don't put "no-console" : false in tslint.json
# tslint will prevent this line and throw an error.
console.log("listening on port " + PORT);
});

App.ts
app.ts hold the core logic of our application including initiating express and configure it.

Create file at “./src/server.ts” and write blow code into:

import express from "express";// used to parse the form data that you pass in the request
import * as bodyParser from "body-parser";
// imports all routes from routes module
import routes from "./routes";
// cors is using to resolve CORS
import cors from "cors";
class App {public app: express.Application;constructor() {
// run the express instance and store in app
this
.app = express();
this.config();
}
private config(): void {// enable cors by adding cors middleware
this
.app.use(cors());
// support application/json type post data
this
.app.use(bodyParser.json());
// support application/x-www-form-urlencoded post data
this
.app.use(bodyParser.urlencoded({extended: false}));
// add routes
this
.app.use("/api/v1", routes);
}
}
export default new App().app;

Routes Module
This is the place that all routes resist regardless of their functionality. they should resist here and if there is big logics they should resit in controller module which I will not cover in this article.

Create file at “./src/routes/index.ts” and write blow code into, we will change its content in next section:

import { Router } from "express";const router = Router();// we will add routes to this default router in futureexport default router;

Write First Route

Routes are some resources which clients will consume, for best practice of designing REST API routing please read this post.

We will write a sample route which will get the “city-name” as query parameter and return the city’s weather status.

Create file at “./src/routes/weather.ts” and write blow code into:

import express = require("express");import { Request, Response } from "express";const router = express.Router();// create get method which will return city weather status.
router.get("/:city", (req: Request, res: Response) => {
const city: string = req.params.city; if (!city) {
return res
.status(400)
.json({
error: true,
messsage: "city name should be filled :("
});
}
let result: { degree: string; status: string };
let resultNotFound: boolean = false;
switch (city.toLowerCase()) {
case "nyc":
result = { degree: "18C", status: "foggy" };
break;
case "stockholm":
result = { degree: "8C", status: "windy" };
break;
case "san-francisco":
result = { degree: "14C", status: "rainy" };
break;
case "tokyo":
result = { degree: "21C", status: "sunny" };
break;
default:
resultNotFound = true;
}
if (resultNotFound) {
return res.json({
error: true,
messsage: "city name not found in DB :("
});
}
else {
return res.json(result);
}
});
export default router;

I highly wanted to discuss about async/await coding and it’s benefit in this section but will discuss about it in near future in another article to cover all the aspects of async/await. this is very trending topic and there are a lot of heat about it.

Refactor “./src/routes/index.ts”:

import { Router } from "express";// *added* import for weather route
import Weather from "./weather";
const router = Router();// *change here to address routes*
router.use('/weather', Weather);
export default router;

That’s it now let see how this will run in next section.


Enable Live Editing for Debugging & Run

There are a lot of ways for live editing at debug runtime such as using grunt or other automation tools but fortunately TypeScript compiler has an option for it, you can use compiler with “ — — watch” parameter to enable watch mode. Watch mode looks for file change in src folder and recompiles the code.

We will use Concurrently, Nodemon and TypeScript compiler -w option to recompile and rerun the Node.

Concurrently + Nodemon + tsc -w = Live Editing Enabled

Nodemon is a tool that helps develop node.js based applications by automatically restarting the node application when file changes in the directory are detected.

Concurrently runs multiple commands concurrently.

First add some npm packages:

# install nodemon and concurrently as global packagesnpm install nodemon concurrently -g

Now let’s change our package.json as below by adding some npm scripts:

{
"name": "project",
"version": "1.0.0",
"description": "",
"main": "./dist/server.js",
"scripts": {
"start": "npm run build && npm run watch",
"build": "rm -rf ./dist/* && npm run build-ts && npm run tslint",
"watch-node": "nodemon ./dist/server.js",
"watch-ts": "tsc -w",
"watch": "concurrently -k -p \"[{name}]\" -n \"TypeScript,Node\" -c \"yellow.bold,green.bold\" \"npm run watch-ts\" \"npm run watch- node\"",
"build-ts": "tsc",
"tslint": "tslint -c tslint.json -p tsconfig.json"
},
"author": "Alireza Alidoust",
"license": "ISC",
"devDependencies": {
"@types/body-parser": "^1.17.0",
"@types/cors": "^2.8.4",
"@types/express": "^4.16.0",
"@types/request": "^2.48.0",
"tslint": "^5.11.0",
"tslint-config-prettier": "^1.15.0",
"typescript": "^3.1.6"
},
"dependencies": {
"body-parser": "^1.18.3",
"cors": "^2.8.4",
"express": "^4.16.4",
"request": "^2.88.0"
}
}

That’s All now lets just run:

npm start
package.json

You can test using Postman:

result when parameter is valid
result when parameter is not valid

This is github repository for this project.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade