In this article I will be explaining how to write a simple express server that is of Production Grade. As you might know that a simple Express server running a Hello World code looks something like this – express = ( ) app = express() port = app.get( , res.send( )) app.listen(port, .log( )) const require 'express' const const 3000 '/' ( ) => req, res 'Hello World!' => () console `Example app listening on port !` ${port} Putting everything in the file is alright for a small hobby project and for development in a local environment, however this is not how the professionals work. index.js Instead a Hello World server written by a professional might look something like this – Yes, this huge project structure only to run a simple Hello World ! In this article I will be explaining how you can too write something like this, that is Production ready, and thus level up on your NodeJS skills. I will be using Typescript for this project, and I encourage you all to do the same! Because, it makes our code Object Oriented as well as easy to understand! Basic Installations & Setup – 1. Create a file by running the command package.json npm init 2. Install Typescript on your system globally Typescript (Installed globally) npm i typescript -g ts-node (Installed globally) npm i ts-node -g 3. Create a tsconfig.json file for the project by running the command tsc --init 4. Install the Other Packages – Express and Cors npm i express cors @types/express Winston Logger for Logging npm i winston @types/winston module-alias for Module Aliases npm i module-alias The package.json file looks something like this – { : , : , : , : , : { : , : , : }, : , : , : { : , : , : , : , : , : }, : { : , : , : }, : { } } "name" "productionserver" "version" "1.0.0" "description" "A Production Ready Server" "main" "./build/index.js" "scripts" "test" "mocha -r ts-node/register tests/**/*.test.ts" "start" "ts-node index.ts" "coverage" "nyc -r lcov -e .ts -x \"*.test.ts\" npm run test" "author" "Dhairya Gada" "license" "ISC" "dependencies" "@types/express" "^4.17.3" "@types/winston" "^2.4.4" "cors" "^2.8.5" "express" "^4.17.1" "module-alias" "^2.2.2" "winston" "^3.2.1" "_moduleAliases" "@root" "." "@utils" "build/src/utils" "@configs" "build/configs" "devDependencies" And tsconfig.json file is as follows – { : { : , : , : [ , , ], : , : , : , : , : , : , : , : , : { : [ ], : [ ], } }, } "compilerOptions" "target" "es5" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */ "module" "commonjs" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */ "lib" "es5" "es6" "dom" "sourceMap" true /* Generates corresponding '.map' file. */ "outDir" "./build" /* Redirect output structure to the directory. */ "strict" true /* Enable all strict type-checking options. */ "moduleResolution" "node" /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ "baseUrl" "./" /* Base directory to resolve non-absolute module names. */ "esModuleInterop" true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ "forceConsistentCasingInFileNames" true /* Disallow inconsistently-cased references to the same file. */ "resolveJsonModule" true "paths" "@utils/*" "src/utils/*" "@configs/*" "configs/*" Planning – The Why ? Before we start developing the application it is always recommended to plan the structure of our code. The code should be organized in such a fashion that it enables you to tackle the following two “if’s” gracefully – You should be able to make revisions to the program quite easily. That is without disturbing the of the existing code and with minimal changes ! 1. IF the requirements change harmony You should be able to build on top of the prevailing code, re-using components wherever possible. Without making any modifications to the already existing code . That is extending without modifying ! 2. IF new requirements are introduced I know that it is quite impossible to be build a structure with such robustness and flexibility, but we should always aim for it. Thus making our code as neat as possible. The How ? 1. S.O.L.I.D PRINCIPLES We can plan our code structure to be clutter-free by following the S.O.L.I.D. principles of Software Design. S – Single Responsibility The Functions and classes should have only one responsibility. That is they should have only a single reason to exist. O – Open to Extension Closed to Modification A Class should be Open to Extension but closed to modification. This can be achieved by the use of Abstract Classes. But there is a catch here! The parent class and the derived class should be such that they satisfy the next principle i.e The Liskov Substitution Principle. Otherwise interfaces should be used. L – Liskov Substitution Principle The statement of Liskov Substitution Principle says that the Instance of a Base Class should be replaceable by the instance of its Derived class. This basically means that the derived class should not introduce a behavioral change in any of it’s methods ! Honestly I found this principle a little tough to understand, however the following YouTube video made it clear to me! I – Interface Segregation The interfaces should not have any elements that their clients do not use. This can be achieved my making multiple smaller meaningful interfaces, rather than a huge one that is only partially helpful. D – Dependency Inversion The interaction between the higher level class and the lower level class should be abstract. And the details of interaction should depend on the abstraction. You can read more on the SOLID Principles from the following resources – Brutally Solid TypeScript Solid Principles Made Easy Is Your Code Solid Enough 2 . APPLICATION LOGIC AND BUSINESS LOGIC SHOULD NEVER BE MIXED The Structure . ├── docs │ └── Readme.md ├── logs │ └── app.log ├── node_modules │ └── … ├── src │ ├── constants │ │ └── StatusConstants.ts │ ├── core │ │ ├── InitializeMiddleware.ts │ │ ├── InitializeRoutes.ts │ │ └── Server.ts │ ├── middleware │ │ ├── CommonMiddleware.ts │ │ └── ErrorHandlingMiddleware.ts │ ├── routes │ │ ├── helloworld │ │ │ └── HelloWorldRouteController.ts │ │ └── AbstractRouteController.ts │ ├── serviceclasses │ │ └── helloworld │ │ └── HelloWorld.ts │ └── utils │ └── logger │ └── Logger.ts ├── tests │ ├── integration │ └── unit │ ├── configs │ ├── LoggerConfig.json │ └── ServerConfig.json │ ├── index.ts ├── package-lock.json ├── package.json └── tsconfig.json └── ecosystem.config.json Basically there are three main folder – └──src All the Code related to project is maintained inside the src folder └──test All the Code related to the testing is maintained inside the tests folder └──configs All the Configurations related to the project are maintained inside the configs folder. Apart from these three the build folder is where the compiled JavaScript Code resides. This Folder and its contents are automatically created by the typescript compiler. Configs folder We want the host and port numbers of our Web Server to be configurable and not hard-coded. Therefore we maintain a JSON file with host and port numbers, from where these two values are loaded. Here is the ServerConfig JSON that I have created for our webserver. { : , : } "host" "localhost" "port" 4000 Src Folder The folder structure of folder is something like – src ├── core │ ├── middleware │ ├── routes │ ├── serviceclasses │ ├── utils │ └── logger │ ├── constants The Code inside src folder can be distinguished into two categories – Application Logic Code and Business Logic Code. BUSINESS LOGIC CODE Business Logic Code resides in the folder serviceclasses In serviceclasses folder we have all the code pertaining to the actual processes that need to be carried out . These logic keeps changing as per the business requirements. For the purpose of our Hello World Server let us assume that the business activity that needs to be carried out is returning a message string of Hello World to the user. Therefore I have created a class and a method for the same. I create a folder called helloworld and inside it a class HelloWorld.ts. HelloWorld.ts { public wishHello(): <string>{ resp = .resolve(resp) } } export class HelloWorld static async Promise let `Hello World!` return Promise Note that how the above code follows the principle of the Single Responsibility from the Solid Principles. The above piece of code has no relation to starting and running the Web-Server or the routes whatsoever. It only exists for a single purpose i.e. to return the string Hello World. The route that calls this business activity is a part of the application logic. Therefore even if the business activity is modified to wishing Hello Universe instead of Hello World the application logic will remain untouched! Application Logic Code core , middleware, routes ,utils and constants are the folders among which the Application Logic Code is distributed. CONSTANTS FOLDER In this folder we will be storing constants that will be used throughout the project. The advantage of creating a separate space for the constants is that they can be used repetitively in the entire project and if any rare scenario occurs where the value needs to be changed, you do not need to go to every class that has used it and separately change it . For the purpose of this project I have created only a single class of constants i.e. status constants – StatusConstants.ts { public code404 = public code404Message = public code200 = } export class StatusConstants static 404 static "Method Not Found" static 200 Now imagine that we have directly used the string "Method Not Found" as a 404 Error Message at several places in our code. And then due to some reason you need to change the message to something else. For that you will need to go to every single code location where you have written "Method Not Found" and change it to your new message ! This all efforts are saved by using a constants file and invoking the message wherever required. UTILS FOLDER Utils folder is where Commonly Used utilities can be placed. Such as String Utilities, Numerical Functions, Error Handling Utilities, Logging Utilities etc I have created a Logging Utility that we will be using in this project. For that I have created a Logger Class inside the logger folder in utils. logger/Logger.ts winston {options} { private logger: winston.Logger private instance: Logger private () { .logger = winston.createLogger({ : [ winston.transports.Console(options.console), winston.transports.File(options.file) ] }) } public getLoggerInstance(){ (!Logger.instance) { Logger.instance = Logger(); } Logger.instance } public getLogger(){ _logger = Logger.getLoggerInstance() _logger.logger } } import from 'winston' import from '@configs/LoggerConfig.json' export class Logger static constructor this transports new new static if new return static let return I have used an NPM package for Winston Logger to create this utility. This package can be installed by running the command . npm i winston @types/winston The configuration that this logger needs is stored in the configs folder that I had discussed above. configs/LoggerConfig.json { : { : { : , : , : , : , : , : , : }, : { : , : , : , : } } } "options" "file" "level" "info" "filename" "../logs/app.log" "handleExceptions" true "json" true "maxsize" 5242880 "maxFiles" 5 "colorize" true "console" "level" "debug" "handleExceptions" true "json" false "colorize" true One more Important thing to note about the Logger Class is that it is . Because we can use the same instance every time it is called, there is no reason for multiple instances to exist! Singleton You can read more about the Singleton Classes from here – https://refactoring.guru/design-patterns/singleton ROUTES FOLDER This is the folder where all the routes of our express server are declared. Note that they are just declared here and not initialized. Initialization of this routes is a different responsibility, hence it is placed in a different folder in a new class. I will be explaining the initialization of the routes when I explain the core folder. The route declarations basically points an API path to it’s corresponding business activity logic which is placed in the serviceclasses folder. Here we have an Abstract Base Class known as AbstactRouteController.ts , all the Specific Route Controllers are derived from this Base Class. AbstractRouteController.ts express = ( ); abstract { router = express.Router(); path!: string; public InitializeController(link: string) { .log(link + .path) .InitializeGet() .InitializePost() } public runService(req: express.Request, : express.Response): <any> { resp.send( + .path + ) } public InitializeGet(){ .router.get( .path, .runService.bind( )).bind( ) } public InitializePost(){ .router.post( .path, .runService.bind( )).bind( ) } } import require "express" export class AbstractRouteController async console this await this await this async resp Promise 'runService Method for ' this 'does not exist !' async this this this this this async this this this this this I create a new folder called helloworld inside which I set up the Route Controller for the helloworld API. helloworld /HelloWorldRouteController.ts { AbstractRouteController } ; {Response,Request} { HelloWorld } ; { StatusConstants } ; { (link:string){ (); .path = ; .InitializeController(link); } public runService(req: Request, : Response): <any>{ response = HelloWorld.wishHello() resp.status(StatusConstants.code200).send(response) } } import from "../AbstractRouteController" import from 'express' import from "../../serviceclasses/helloworld/HelloWorld" import from "../../constants/StatusConstants" export class HelloWorldRouteController extends AbstractRouteController constructor super this '/helloworld' this async resp Promise let await As it can be seen that when the API /helloworld is called, using either get or post method, the corresponding Route Controller i.e . the HelloWorldRouteController calls it’s respective business activity i.e. HelloWorld.wishHello() and returns its response. Also, note that how the code follows Open-Closed principle. The code for a new route controller is created by extending an existing abstract base class, without modifying any prevailing code. The parent class and the child classes satisfy the Liskov Substitution principle because the base class and the derived class have the same behavior and can be substituted for each other! MIDDLEWARE As the name of the folder suggests, this is where the middleware components are defined. Following is the code for common middleware – CommonMiddleware.ts { Express } { Logger } ; bodyParser = ( ) cors = ( ); { app: Express (_app: Express) { .app = _app } public useBodyParser() { .app.use(bodyParser.json()); } public useURLencoded() { .app.use( bodyParser.urlencoded({ : }) ); } public useCors() { .app.use(cors()); } public logRequests() { logger = Logger.getLogger() .app.use( { logger.info(req.originalUrl); done(); }); } } import from 'express' import from '../utils/logger/Logger' let require 'body-parser' let require 'cors' export class CommonMiddleware constructor this async this async this extended true async this async let this ( ) => req, res, done Note that how the logger class is used to log all the incoming requests with the help of express middleware! I have also created a separate class for the ErrorHandlingMiddleware – ErrorHandlingMiddleware.ts { Express } { Response, Request } { StatusConstants } { app: Express (_app: Express) { .app = _app } public handle404Error() { .app.use( { resp.status(StatusConstants.code404).send(StatusConstants.code404Message) }) } } import from 'express' import from 'express' import from '../constants/StatusConstants' export class ErrorHandlingMiddleware constructor this async this ( ) => req: Request, resp: Response CORE FOLDER Core folder is where everything starts to come together. It is where the most important classes with respect to the starting the application are stored. Initializing the middlewares – InitializeMiddleware.ts { Express } { CommonMiddleware } { ErrorHandlingMiddleware } { public InitializeCommonMiddleware(app :Express){ middleware = CommonMiddleware(app) middleware.useBodyParser() middleware.useURLencoded() middleware.useCors() } public InitializeErrorHandlingMiddleware(app :Express){ errorMiddleware = ErrorHandlingMiddleware(app) errorMiddleware.handle404Error() } } import from 'express' import from '../middleware/CommonMiddleware' import from '../middleware/ErrorHandlingMiddleware' export class InitializeMiddleWare static async let new await await await static async let new await Initializing the Route Controllers that we had written in the Routes folder – { Express } { HelloWorldRouteController } { AbstractRouteController } { public Initialize(app: Express, : string) { routes = .getRoutes(link) routes.forEach( { app.use( , rc.router) }) } public getRoutes(link: string): < <AbstractRouteController>> { routes: <AbstractRouteController> = [] routes.push( HelloWorldRouteController(link)) .resolve(routes) } } import from 'express' import from '../routes/helloworld/HelloWorldRouteController' import from '../routes/AbstractRouteController' export class InitializeRoutes static async link let await this => rc "/" static async Promise Array let Array new return Promise Note that in this class how Dependency Inversion principle comes into picture. Here details regarding the routes is passed to a Higher Class (Express Module) in the form of an abstraction where is an instance of ! rc.router rc AbstractRouteController Finally the code to start all the middleware, routes and the server itself is written in Server.ts server.ts express = ( ) {Express} { InitializeMiddleWare } ; { InitializeRoutes } ; * ServerConfig { app :Express= express(); host = ServerConfig.host port = ServerConfig.port link = +host+ + port.toString() InitializeMiddleWare.InitializeCommonMiddleware(app) InitializeRoutes.Initialize(app,link) InitializeMiddleWare.InitializeErrorHandlingMiddleware(app) app.listen(port, host, () => { .log( ) }) } var require 'express' import from 'express' import from './InitializeMiddleware' import from './InitializeRoutes' import as from '@configs/ServerConfig.json' export async ( ) function server let let let let "http://" ":" await await await console `Server started listening at on port.` ${host} ${port} Finally the Index.ts looks in the root folder looks something like this – ( ) { server } ; server() require 'module-alias/register' import from "./src/core/Server" Module Aliases You might have noticed that at some places the files have been imported with an @ in front of them. Those are Module Aliases. Module Aliases save us from writing longer paths like ../../configs/ServerConfig.json by simply using @configs/ServerConfig.json To use Module Aliases we use a package called module-alias which can be installed by the command npm i module-alias. After Installing this package, the aliases are declared in the tsconfig.json file with the following declarations – : { : [ ], : [ ], } "paths" "@utils/*" "src/utils/*" "@configs/*" "configs/*" And in package.json file as – : { : , : , : } "_moduleAliases" "@root" "." "@utils" "build/src/utils" "@configs" "build/configs" And by adding the following line on the top of the index.ts file – require ('module-alias/register') Look up a detailed tutorial on setting up the module aliases here – or https://dev.to/larswaechter/path-aliases-with-typescript-in-nodejs-4353 https://medium.com/@caludio/how-to-use-module-path-aliases-in-visual-studio-typescript-and-javascript-e7851df8eeaa That's It Folks In case you found this post to be informative or helpful do leave a like on it, this encourages me a lot. Also, if you have any suggestions or any questions feel free to leave a comment below! Click here to read the next part of this article where I explain how to write tests and finally run the webserver! Click here to visit the code used in this post on GitHub Previously published at https://dev.dhairyag.com/writing-a-production-ready-express-server-like-a-pro-1/