BrilworksarrowBlogarrowTechnology PracticesarrowOptimize Your Nest.js App Performance With These Practices

Optimize Your Nest.js App Performance With These Practices

Lavesh Katariya
Lavesh Katariya
April 26, 2023
Clock icon7 mins read
Calendar iconLast updated July 29, 2024
banner-nest-js-app-performance
Quick Summary:- Discover expert tips and strategies to Optimize Your Nest.js App Performance. In this guide, we have handpicked proven techniques to improve performance. From load balancing to web worker utilization, learn how to overcome bottlenecks and build a resilient, scalable architecture.

In the modern era of rapid technological advancement, users tend to lose interest in using an application if it doesn’t load quickly. Since there are multiple alternatives available for each app, users are more likely to switch to another app if you don’t optimize your app.

As per a study, a B2B site that opens in 1 second has 5x conversion than site that loads in 10 seconds. 

A survey shows that if an app takes longer than three seconds to load, the probability of losing users increases by 40%. The optimization of an app is an essential factor that plays a critical role in the app’s success.

banner-image

Source: Nielsen Norman Group

Nest.JS applications come equipped with a wide range of tools that developers can leverage. In this article, we will be exploring ways in which you can enhance your Nest.JS app’s performance by optimizing it effectively. 

What is Nest.JS?

Nest.JS ranks among the popular JavaScript frameworks that offer a robust set of tools for creating server-side applications, and native support for TypeScript. All these features elevates the development process to a higher level. 

You can use Nest JS to develop sophisticated, high-performing, and scalable server-side applications. It provides you with a modular structure which makes it easier to organize the code. Additionally, it offer a wealth of tools and plugins that you can use to optimize your app. However, the overall performance of the application ultimately depends on the level of effort put in by the developer.

Here are some practices that can help you optimize your NestJS application to improve its performance and efficiency.

Implement Caching

Caching refers to the process of temporarily storing frequently accessed data in high-speed storage — cache memory. By activating caching, you can reduce the data retrieval time.  Here’s how you can implement caching in your application.

1. Install Cache-Manager

NestJS provides a solution for implementing caching using cache-manager. To install it, run the following command: 

$ npm install @nestjs/cache-manager cache-manager

This cache manager offers a unified interface that supports various cache storage providers beyond just the default in-memory option.

To enable in-memory caching, run the following command: 

import { Module } from '@nestjs/common';
import { CacheModule } from '@nestjs/cache-manager';
import { AppController } from './app.controller';

@Module({
  imports: [CacheModule.register()],
  controllers: [AppController],
})

export class AppModule {}

To learn more about caching, you can explore additional resources and consider switching to alternative service providers.

Choose Appropriate Dependency Injection Library

Nest.JS provides developers with the sweet nectar of dependency injection. This design pattern helps write a maintainable codebase by injecting dependencies as needed. But this blessing can also turn sour if not used properly.

NestJS provides built-in support for dependency injection (DI), however, you can look beyond in-build options and use other DI libraries. There are several popular DI libraries that are compatible with NestJS, including:

1. InversifyJS 

InversifyJS is a powerful and flexible DI library that supports advanced features such as constructor parameter injection and named dependencies. It has a large community and well-documented resources for developers. You can use it with NestJS by installing the `@nestjs/inject` package and importing the `InversifyAdapter` from it.

2. Awilix

Awilix is a lightweight DI library that emphasizes simplicity and ease of use. It has a simple API and supports many of the same features as other DI libraries, such as constructor injection and property injection. You can use Awilix with NestJS by installing the `awilix` package and using the `awilix-express` integration library.

3. TypeDI

TypeDI provides a simple and intuitive API for defining and injecting dependencies. You can use TypeDI with NestJS by installing the `typedi` package and importing the `TypeDIAdapter` from the `@nestjs-typeorm` package.

4. tsyringe

tsyringe is a lightweight and flexible DI library that supports many advanced features such as circular dependencies and conditional injection. It has a simple and intuitive API, and you can find comprehensive documentation to work with the tsyringe. To use tsyringe with NestJS, you can install the `tsyringe` package and importing the `TsyringeAdapter` from the `@nestjs-modules/injection` package.

If you’re just getting started with NestJS and don’t have any particular requirements, the built-in DI system is a good choice as it is lightweight and easy to use.

banner-image

Use Compression

The more code you have, the slower application will become. Therefore to prevent your app performance from getting bogged down by heavy code, you can use available compression techniques  to reduce the size of HTTP responses, JavaScript, and CSS files. 

This technique includes Gzip, deflate, and Brotli compression. They help you reduce the weight of your code. 

To use compression in NestJS, you can take the following steps:

1. Install the compression package:

npm install --save compression

2. Import the CompressionMiddleware from compression:

import * as compression from 'compression';
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  // Use the compression middleware
  app.use(compression());

  await app.listen(3000);
}
bootstrap();

This file imports the compression middleware and adds it to the application using the app.use() method.

By default, the compression() middleware will use the gzip compression algorithm. You can also pass in options to customize the behavior of the middleware, such as the threshold at which responses will be compressed, using the following syntax:

app.use(compression({
threshold: 512 // set the threshold to 512 bytes
}));

With compression enabled, responses from your Nest.JS application will be compressed before they are sent to the client, reducing the amount of data that needs to be transferred over the network and improving performance.

Optimize database queries

One effective method is to minimize the number of queries by utilizing query batching and compatible ORM frameworks that enable streaming and enhance the overall database process. Rather than retrieving all available data, reducing the amount of data fetched can also minimize the workload on the database and optimize its operations.

Revamp your Algorithm 

To optimize an app’s performance, it’s essential to fine-tune its algorithm, which entails creating a series of instructions that direct the code on how to operate. 

The algorithm’s defined steps have a direct impact on the app’s performance, as they dictate the optimal times and conditions for the code to perform specific tasks. 

It is crucial to evaluate the algorithm’s optimization level to enhance the app’s overall performance. The algorithms inform the code when to execute particular tasks and when not to, enabling it to work more efficiently. Essentially, the algorithm acts as a roadmap for the code, guiding it on how to function for optimal efficiency.

Leverage Web Workers

When executed efficiently, multitasking can enhance the performance of an application by allowing for faster execution of code. One way to achieve this in JavaScript is through the use of web workers, which enable multitasking and can run without interfering with the main event loop.  This approach can be particularly beneficial for resource-intensive tasks that do not require user interaction.

Web Workers are a way to run scripts in a separate thread from the main thread of a web application. Nest.JS provides built-in support for Web Workers through the worker_threads module that comes with Node.js. You can use this module to create and manage worker threads in your NestJS application.

Here is an example of how to use Web Workers in NestJS:

1. Create a new worker file:

// worker.js
const { parentPort } = require('worker_threads');

parentPort.on('message', (message) => {
  console.log(`Worker thread received message: ${message}`);
  parentPort.postMessage(`Worker thread received message: ${message}`);
});

This file creates a worker thread that listens for messages sent from the main thread, logs the message to the console, and then sends a response back to the main thread.

2. In your NestJS controller, create a new worker thread:

// app.controller.ts
import { Controller, Get } from '@nestjs/common';
import { Worker } from 'worker_threads';

@Controller()

export class AppController {
  @Get()
  async getHello(): Promise<string> {
    return new Promise((resolve) => {
      const worker = new Worker('./worker.js');
      worker.on('message', (message) => {
        console.log(`Main thread received message: ${message}`);
        resolve(message);
      });
      worker.postMessage('Hello from main thread!');
    });
  }
}

This code creates a new worker thread and sends a message to it. When the worker thread receives the message, it sends a response back to the main thread, which resolves the promise and returns the response to the client.

Note that the worker thread is created within a promise to make it easier to handle the asynchronous communication between the threads.

3. Start the application:

npm run start

This will start the NestJS application and create a new worker thread to the root endpoint. The worker thread will receive the message, log it to the console, and send a response back to the main thread, which will then return the response to the client.

Web workers can be a powerful tool for improving the performance and scalability of web applications, especially for long-running or computationally-intensive tasks. By using Web Workers in Nest.JS, you can take advantage of this technology to build faster, more efficient applications.

banner-image

Implement Load balancing

Load balancing is a technique of evenly distributing incoming network traffic among multiple servers to prevent any single server from becoming overloaded and causing the application to become unresponsive and sluggish. 

NestJS provides a built-in module, “@nestjs/platform-socket.io”, for Socket.IO-based applications. Additionally, you can use third-party load balancers like NGINX or cloud-based balancers such as WS Elastic Load Balancer or Google Cloud Load Balancer to accomplish this task.

Here’s an example of how to implement reverse proxy load balancing using ExpressJS in NestJS:

1. Install the @nestjs/platform-express package and http-proxy-middleware package:

npm install --save @nestjs/platform-express http-proxy-middleware

2. Import ExpressAdapter from @nestjs/platform-express:

import { ExpressAdapter } from '@nestjs/platform-express';

3. Create a new main.ts file that initializes the NestJS application using the ExpressAdapter:

import { NestFactory } from '@nestjs/core';
import { ExpressAdapter } from '@nestjs/platform-express';
import { AppModule } from './app.module';
import * as express from 'express';
import { createProxyMiddleware } from 'http-proxy-middleware';

async function bootstrap() {
  const server = express();
  const app = await NestFactory.create(
    AppModule,
    new ExpressAdapter(server)
  );

  const servers = [
    { target: '<http://localhost:3001>' },
    { target: '<http://localhost:3002>' }
  ];

  servers.forEach(server => {
    server.use('/api', createProxyMiddleware({
      target: server.target,
      changeOrigin: true,
      pathRewrite: { '^/api': '' }
    }));
  });

  await app.listen(3000);
}

bootstrap();

This file creates an ExpressJS application using the ExpressAdapter from @nestjs/platform-express.
The createProxyMiddleware function from http-proxy-middleware is used to create a reverse proxy for each upstream server. The changeOrigin option is set to true to ensure that the origin header of the incoming request is modified to match the target server. The pathRewrite option is used to remove the /api prefix from the incoming requests before forwarding them to the upstream server.

The reverse proxy is mounted on the /api path, so any incoming requests to /api will be forwarded to one of the upstream servers.

4. Start the reverse proxy server:

node server.js

This will start the reverse proxy server on port 3000.

5. Start the upstream servers:

node server1.js
node server2.js

This will start two upstream servers on ports 3001 and 3002.

That’s it. You now have a reverse proxy load balancer that will distribute incoming traffic across multiple servers using ExpressJS in Nest.JS.

Enable Lazy Loading

By enabling lazy loading, you can delay the initialization of objects and loads only the necessary components. It will speed up the application’s loading time.

Rather than loading all objects at once, it allows the application to load the necessary data.

To enable lazy loading in Nest.JS, you need to use the loadChildren property in the @Module decorator. Here’s an example:

import { Module } from '@nestjs/common';

@Module({
  imports: [
    // ...
    {
      path: 'users',
      loadChildren: () => import('./users/users.module').then(m => m.UsersModule),
    },
  ],
})

export class AppModule {}

In this example, the loadChildren property is used to import the UsersModule lazily when the users path is accessed. The loadChildren property takes a function that returns a promise that resolves to the module to be loaded. In this case, we use the import() function to load the UsersModule dynamically.

You can also use the canLoad method in the route guard to control the access of loading the module. By default, the canLoad method returns true, but you can implement your own logic to determine whether the module should be loaded or not.

import { CanLoad, Route, UrlSegment } from '@nestjs/common';

@Injectable()

export class AuthGuard implements CanLoad {
  canLoad(route: Route, segments: UrlSegment[]): boolean {
    // Implement your logic here
    return true;
  }
}

Then you can use this guard in the @Module decorator.

import { Module } from '@nestjs/common';
import { AuthGuard } from './guards/auth.guard';

@Module({
  imports: [
    // ...
    {
      path: 'users',
      canActivate: [AuthGuard],
      canLoad: [AuthGuard],

      loadChildren: () => import('./users/users.module').then(m => m.UsersModule),
    },
  ],
})

export class AppModule {}

In this example, the AuthGuard is used as both a canActivate and canLoad guard. It ensures that the user is authenticated before loading the module.

banner-image

Conclusion

This article explores various effective techniques to enhance the performance of Nest.JS applications, which can be beneficial for developers at all levels. 

We have highlighted some commonly used Nest js best practices that can make your app more well-rounded. If you have an interest in learning more about NestJS development or require assistance in developing an app, please feel free to reach out to us.

At Brilworks, we offer full-time NodeJS developers who specialize in NestJS-based application development services. Our experienced team can efficiently develop and publish a well-optimized, industry-level application tailored to your specific requirements. If you are in need of these services, please do not hesitate to contact us.

FAQ

Reducing load time in a Nest.js app involves several optimizations. Implementing caching for frequently accessed data is crucial. Optimizing database queries, minimizing network requests, and compressing response data can significantly improve performance. Leveraging CDNs for static assets and using load balancing for high traffic can also help. Profiling your application to identify bottlenecks is essential for targeted optimization.

To monitor and analyze Nest.js app performance, consider using tools like APM (Application Performance Monitoring) solutions. These tools provide insights into response times, error rates, and resource utilization. Additionally, you can implement custom performance metrics and logging. Profiling your code and using performance testing frameworks can help identify performance bottlenecks. Regularly monitoring key performance indicators (KPIs) is essential for maintaining optimal performance.

Efficient API call handling in Nest.js involves several strategies. Implementing caching for frequently accessed data reduces the load on your backend. Using appropriate HTTP methods (GET, POST, PUT, DELETE) and status codes enhances API clarity. Validating input data and handling errors gracefully improves API reliability. Consider using circuit breakers to protect against failures and rate limiting to prevent abuse. Optimizing database queries and using asynchronous operations can further enhance performance.

Lavesh Katariya

Lavesh Katariya

With over 8 years of experience in the MERN stack, I specialize in building robust and scalable web applications. Proficient in MongoDB, Express.js, React.js, and Node.js, my passion for clean code and innovative problem-solving ensures high-quality solutions.

You might also like

Get In Touch


Contact us for your software development requirements

Brilliant + Works

Hello, we are  BRILLIAN’S.Trying to make an effort to put the right people for you to get the best results. Just insight !

We are Hiring hiring-voice
FacebookYoutubeInstagramLinkedInBehanceDribbbleDribbble

Partnerships:

Recognized by:

location-icon503, Fortune Business Hub, Science City Road, Near Petrol Pump, Sola, Ahmedabad, Gujarat - 380060.

© 2024 Brilworks. All Rights Reserved.