Building Scalable and Maintainable Node.js Applications with TypeScript and OOP Principles
Tags: node, typescript, oop, opensource
Published: 2023-03-19 17:03:11

**TypeScript** is a powerful language that adds a layer of type safety and code organization to JavaScript. It has become increasingly popular in the Node.js community due to its ability to provide better code quality, maintainability, and scalability. In this article, we will explore how to use TypeScript with Node.js using Object-Oriented Programming (OOP) principles.

**Object-Oriented Programming** is a programming paradigm that focuses on creating objects that encapsulate data and behavior. In TypeScript, we can create classes that define the structure and behavior of objects. Classes can have properties, methods, and constructors, and can inherit from other classes.

#Let's build an application
**1.** First we need to make a new folder and initialize npm in this folder by the following commands:

```
mkdir myproject
cd myproject
npm init -y
```

---

**2.** To use TypeScript with Node.js, we first need to install the TypeScript compiler and other dependencies. We can do this using the npm package manager by running the following command:

###Development dependencies
```
npm i -D typescript 
tsc-watch
eslint 
prettier 
eslint-config-prettier
eslint-plugin-prettier
@typescript-eslint/parser 
@typescript-eslint/eslint-plugin
@types/node @types/express

```
###Dependencies
```
npm i express dotenv
```

•	 **typescript** is the package that adds TypeScript support to the project.

•	**tsc-watch** is a package that allows you to watch for changes to TypeScript files and automatically recompile them.

•	 **eslint** is a package that provides linting functionality for the project.

•	 **prettier** is a package that provides code formatting functionality for the project.

•	**eslint-config-prettier** and **eslint-plugin-prettier** are packages that configure ESLint to work with Prettier.

•	 **@typescript-eslint/parser** and **@typescript-eslint/eslint-plugin** are packages that provide ESLint rules specific to TypeScript.

•	 **@types/node** and **@types/express** are packages that provide TypeScript definitions for the Node.js and Express libraries.

•	**express** is a popular Node.js web framework that simplifies the process of building web applications. It provides a set of features for handling HTTP requests and responses, routing, middleware, and more. By using express, developers can create scalable and maintainable web applications with ease.

•	**dotenv** is a package that allows you to load environment variables from a .env file into your Node.js application. This is useful for storing sensitive information such as API keys, database credentials, and other configuration settings. By using dotenv, developers can keep their application secrets secure and separate from the application code.


By installing these packages, developers can benefit from TypeScript's type-checking, linting, and code formatting features, which can help catch errors early and make the codebase more maintainable.

---

**3.** So you can make the structure of the project like this or you can structure it as you want:

```
├── node_modules/ 
├── src/
│   ├── interfaces/
│   ├── controllers/
│   ├── middleware/
│   ├── models/
│   ├── routes/
│   ├── app.ts        --------> Demo of this file will be in the 
│   │                           next point same for server.ts
│   └── server.ts     
├── tests/         
│   ├── unit/
│   └── integration/
├── .env             -------->  contains environment 
│                               variables used by the 
│                               application, loaded in through 
│                               the dotenv package.
│
├── .eslintrc.json   --------> .eslintrc.json and .prettierrc
│                              contain configuration for ESLint 
│                              and Prettier, respectively, which 
│                              are used for  code linting and 
│                              formatting.
│                             
├── .prettierrc
├── package.json
├── tsconfig.json     --------> contains the configuration for 
│                              TypeScript, which includes 
│                             information on how to transpile the 
│                             TypeScript code into JavaScript
│                             that can be run by Node.js.
└── README.md
```

---

**4.** App.ts file will be like this:

```
import express , { Application } from 'express';
import mongoose from 'mongoose';
import Controller from '@/utils/interfaces/controller.interface';
import ErrorMiddleware from '@/middlewares/error.middleware';


class App {
  public expess: Application;
  public port :number;

  constructor(controllers : Controller[] , port : number) {
    this.expess = express();
    this.port = port;
    // THE ORDER OF THESE FUNCTIONS IS IMPORTANT 
    this.initializeDatabaseConnection();
    this.initializeMiddleware();
    this.initializeControllers(controllers);
    this.initializeErrorHandling();
  }

 private initializeDatabaseConnection() : void {
    const { MONGO_USERNAME, MONGO_PASSWORD, MONGO_PATH } = process.env;
mongoose.connect(`mongodb://${MONGO_USERNAME}:${MONGO_PASSWORD}${MONGO_PATH}`);
  }

  private initializeMiddleware() : void {
    this.expess.use(express.json());
    this.expess.use(express.urlencoded({ extended: true }));
  }

  private initializeControllers(controllers : Controller[]) : void {
    controllers.forEach((controller : Controller) => {
      this.expess.use('/api', controller.router);
    })
  }

  private initializeErrorHandling() : void {
    this.expess.use(ErrorMiddleware);
  }

  public listen() : void {
    this.expess.listen(this.port, () => {
      console.log(`App listening on port ${this.port}`);
    })
  }

}

export default App;
```
Where Controller interface will be like this:

```
import { Router } from 'express';

interface Controller {
  path : string;
  router : Router;
}

export default Controller;
```

and ErrorMiddleware will be like this:

```
import { Request , Response , NextFunction } from "express";

function ErrorMiddleware(error: Error  ,req: Request, res: Response, next: NextFunction) : void {
  const status = error.status || 500;
  const message = error.message || "Something went wrong";

  res.status(status).send({
    status,
    message
  })

}
export default ErrorMiddleware;
```
---

**5.**  Every controller will implement Controller interface like this :

```
import { Router, Request, Response, NextFunction } from 'express';
import Controller from '@/interfaces/controller.interface';

class ControllerExample implements Controller {
    public path = '/posts';
    public router = Router();

    constructor() {
        this.initializeRoutes();
    }

    private initializeRoutes(): void {
        this.router.post(
            `${this.path}`,
            this.HttpHandle
        );
    }

    private HttpHandle =(
        req: Request,
        res: Response,
        next: NextFunction
    ): Promise<Response | void> => {
       return res.json({message : "success"})
    };
}

export default ControllerExample;
```

---

**6.**  **Finally** server.ts file will be like this:

```
import 'dotenv/config';
import App from './app';
import ControllerExample from '@/controllers/ControllerFile';


const app = new App(
    [new ControllerExample()],
    Number(process.env.PORT)
);

app.listen();
```


The first line imports and loads the environment variables specified in a .env file using the dotenv package.

An instance of the **App class** is created with an array of controllers and it has one controller for now (ControllerExample) and a port number specified in the environment variables.

Finally, the listen() method of the App instance is called to start the server and listen for incoming requests.

---

This code demonstrates a clean and organized way to start a server for a Node.js application built with TypeScript, with controllers separated by resources and environment variables defined in a .env file.

---

Thank you for taking the time to read my article and hope all of you learn something from it.

Follow me on LinkedIn : [Click here](https://www.linkedin.com/in/omar3ain/)