Kickstart your Node-Typescript application with Express

Sailesh Subramanian
6 min readAug 2, 2020

A quick starter project for your next Node-Typescript application with express

This article helps you to kickstart your REST APIs development in Node and Typescript. This tutorial covers project layer structure, lint configuration, logging configurations, build setup, and docker containerization for a Node-Typescript Rest API project development. The second part of this tutorial will cover Mongo DB connection, Setting up Nginx as reverse proxy and SSL.

This tutorial does not dive deeper into basic concepts of API development or the tools/ libraries, that would be too lengthy . This tutorial focusses only on the configuration tools and the layered structure of the NODE-Typescript project which can guide you in your next project development. You can read a comprehensive blog on REST APIs from here.

Node.js is a JavaScript runtime environment that runs server-side. Inside that environment, we can use JavaScript to build our REST APIs and invoke external services through their APIs. This fact helps the front end developers to do the REST API development and have the entire project codebase in a single programming language.

Node.js is designed for building scalable network applications. It is also relatively simple to set up on a local machine, and you can have your server running with a few lines of code. NodeJS combined with Typescript can help you build robust REST APIs using express framework. If you are a beginner in Typescript you can read more about the concepts from here.

Lets Dive in

Make sure you have Node installed in your system. Check your Node version using the command node --version.

As always, start with npm init -y. Now we need to add our Express framework which helps to easily develop our REST APIs. You could use any node framework, Fastify is a great option.

npm i express

Let’s add our development dependencies to our project.

npm i -D typescript nodemon eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin @types/express @types/node

A brief description on each of the dependencies

  • nodemon— A tool that helps in node application development by automatically detecting changes in the directory and restart the application.
  • eslint — linter tool available to enforce coding guidelines.
  • @typescript-eslint/parser, @typescript-eslint/eslint-plugin — helper plugin to provide eslint support to Typescript.
  • @types/express, @types/node — Type definition files for express and node respectively.

Now we can generate the tsConfig.json. Why? It specifies the directory is a root of the typescript project and the compiler runs based on the configurations given in the tsConfig.json.

npx tsc --init . It generates the tsconfig.json with all the configurations. You can choose to comment and uncomment necessary options. Here is a sample

{
"compilerOptions": {
"module": "commonjs",
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"target": "es6",
"noImplicitAny": true,
"moduleResolution": "node",
"sourceMap": true,
"outDir": "./dist",
"rootDir":"./src",
"baseUrl": ".",
"paths": {
"*": [
"node_modules/*"
]
}
},
"include": [
"src/**/*"
]
}

Linting

Create the .eslintrc file in the root folder. Copy the below code into .eslintrc file.

{
"parser": "@typescript-eslint/parser",
"extends": ["plugin:@typescript-eslint/recommended"],
"parserOptions": {
"ecmaVersion": 2018,
"sourceType": "module"
},
"rules": {
"semi": ["error", "always"],
"quotes": ["error", "double"],
"@typescript-eslint/explicit-function-return-type": "off",
"@typescript-eslint/no-explicit-any": 1,
"@typescript-eslint/no-inferrable-types": [
"warn", {
"ignoreParameters": true
}
],
"@typescript-eslint/no-unused-vars": "warn"
}
}

Also create the .eslintignore to ignore the unnecessary files.

/node_modules/*# build artifactsdist/*coverage/*# data definition files**/*.d.ts# custom definition files/src/types/

So far we were doing some configuration setup. Let us make use of these configurations to run a sample server. Create app.ts and import express.

import express, {Application , Request, Response} from "express";
import http from "http";
const app: Application = express();
const server = http.createServer(app);
app.get("/", (req: Request, res: Response) => { res.json({ "greeting": "Hello, Good Morning !" });});const PORT = 5000;server.listen(PORT);server.on("listening", () => { console.info("server up listening");});

Modify the package.json scripts block.

"scripts": {   "start": "node dist/app.js",   "dev": "nodemon -x ts-node src/app.ts",   "build": "tsc -p ."}

Run the command npm run start:dev . Open up the browser and go to http://localhost:5000/ . You can see the sample response from the path / as {"greeting": "Hello, Good Morning !" } .

Make any changes in theapp.ts file and refresh the tab , you will see the nodemon doing the trick. The build command transpiles the typescript code and generate the JavaScript code inside the dist folder.

Folder Structure

Let us now modify the folder structure to create a structured architecture for development. You will find the project in this Github repository.

folder structure

For the time being I have omitted using any db as source of data but used data.ts . The sample code base consists of a GET, POST, UPDATE, and DELETE APIS. The core layers are stated below.

  • Controllers -Handles the business logic
  • Services - Handles the DB operations
  • Routes- Handles the API route
  • Middleware - custom middleware functions to access requests and responses.

Logging

Winston is robust logging library that can be used for Node API development.

npm i winston

Add the winston configurations in logger.ts inside the startup folder. The transports configurations allow us to write logs and exceptions to respective files.

const winstonLogger = winston.createLogger({
format: combine(
timestamp(),
prettyPrint(),
),
transports: [
new transports.Console(),
new transports.File({ filename: path.join(__dirname, "../logfile.log") }),
],
exitOnError: false,
});
winstonLogger.exceptions.handle(
new transports.File({ filename: path.join(__dirname, "../exceptions.log"), level: "error" })
);

Middleware

These functions help us to access requests and responses. There are default express middleware functions available to help the development activities. Middleware can be application level, route level middleware, Error-handling middleware, and third party middleware. Run the following command to install some of the useful middleware functions and add them to the project.

npm i cors helmet body-parser morgan express-async-errors

Morgan is logger middleware to log HTTP requests. Helmet helps you secure your Express apps by setting various HTTP headers. To know more about the express middleware functions click here

There are custom middleware functions added in the project in case you are not interested in third-party middleware functions.

Docker

Docker is a platform that allows developers to build, test, and deploy applications quickly. It packages the applications to small units called containers. We can have Dockerfile corresponding to each environment based on the project requirements. Docker-compose helps us manage the containers as a single service. The npm commands to run the node server can be specified in the Dockerfile. Please see the docker installation documentation here

FROM node:14.3.0WORKDIR /usr/appCOPY ./package*.json ./RUN npm ciCOPY ./ ./CMD ["npm","run","start"]

You can have a base configuration specified in the docker-compose.yml. The docker-compose.override.yml overrides or extends the default compose yml file. This could be utilized for your local development. Create a separate docker-compose.prod.yml for production purposes.

You can run the below command for local development. You can pass the environment variables either inside the yml file or via build command -e VARIABLE_NAME=..

docker-compose -f docker-compose.yml -f docker-compose.override.yml up -d --build

For the production purpose , run the command:

docker-compose -f docker-compose.yml -f docker-compose.prod.yml up -d --build

In all my software engineering experience every time I ran npm install it was for a front end project development. It was really exciting to try my hands on Node Rest API development. Any improvements and suggestions are most welcome. Happy Coding!!.

Special thanks to Prasanth Kumar Ayyappan, Sanal, and Vijay Sarin.

--

--

Sailesh Subramanian

Front End Engineer @ Tata Consultancy Services Ltd. Cricket enthusiast. Small time writer.