Mastering Lazy-Loading in NestJS: Enhancing Application Performance

Joosep Wong
Dev Genius
Published in
4 min readJan 4, 2024

--

Happy 2024!

After a December so busy it turned my calendar into a game of Tetris, here I am, embracing the art of laziness! But fear not, dear developers, for in the developer’s world, ‘lazy’ isn’t just a couch potato lifestyle — it’s a clever strategy.

Today I’ll show you how being ‘lazy’ can turbocharge your application’s performance. So, let’s dive into the paradoxical universe where laziness equals efficiency. :D

Lazy-loading module is a technique that allows you to load modules only when they are needed, rather than loading them all at the start of your application. This can improve the performance and efficiency of your application, as it reduces the initial loading time, memory usage, and bandwidth consumption.

Lazy-loading can also help you avoid unnecessary dependencies and errors, as you only load the modules that are relevant to your current context.

I’d like to show you how lazy loading works at the route level and the service level.

I. Lazy-loading at the Routing Level

Setting Up a Basic NestJS Project

Start by creating a new NestJS project or using an existing one.

nest new my-nest-project

Creating a Lazy-Loading Module

Generate a new module, LazyModulewhich we will lazy-load.

nest generate module lazy

Define the module in lazy.module.ts.

Configuring Lazy-Loading in AppModule

In your app.module.ts, set up lazy loading for LazyModule using the RouterModule.

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

@Module({
imports: [
RouterModule.register([
{
path: 'lazy',
loadChildren: () => import('./lazy/lazy.module').then(m => m.LazyModule),
},
]),
// other imports
],
// controllers, providers
})
export class AppModule {}

Testing the Lazy-Loading

Access the route (e.g., http://localhost:3000/lazy) and verify that LazyModule loads only upon request.

II. Dynamic Lazy-Loading in a Service

Using LazyModuleLoader

In a different scenario, you might need to load a module dynamically within a service. Here’s how to implement it:

Implementing the PrimaryModule and Service

In your primary.module.ts, inject LazyModuleLoader in a service, PrimaryService.

// primary.service.ts
import { Injectable } from '@nestjs/common';
import { LazyModuleLoader } from '@nestjs/core';

@Injectable()
export class PrimaryService {
constructor(private lazyModuleLoader: LazyModuleLoader) {}

async loadLazyModule(): Promise<void> {
const { LazyModule } = await this.lazyModuleLoader.load(() => import('./lazy/lazy.module'));
// Use LazyModule
}
}

Triggering the Lazy-loading

The loadLazyModule method can be invoked based on specific conditions or actions within your application.

Real-World Scenario: Implementing Feature Flags

Consider a scenario where certain features of your application are behind feature flags. You can use LazyModuleLoader to load modules containing these features only when the feature flag is enabled.

// feature-flag.service.ts
import { Injectable } from '@nestjs/common';
import { LazyModuleLoader } from '@nestjs/core';

@Injectable()
export class FeatureFlagService {
constructor(private lazyModuleLoader: LazyModuleLoader) {}

async loadFeatureModule(featureName: string): Promise<void> {
if (featureName === 'newFeature') {
await this.lazyModuleLoader.load(() => import('./feature/feature.module'));
// Feature module loaded
}
}
}

Conclusion

Lazy-loading in NestJS can significantly improve your application’s performance and resource management. Whether through route-based lazy loading or dynamic module loading within a service, this technique offers flexibility and efficiency in managing large applications.

There might be cases where certain modules are only needed under specific runtime conditions or configurations. For instance, in large-scale applications dealing with message queues or background job processing, lazy-loading can be very advantageous. For instance, if certain queue handlers are only required under specific circumstances (such as processing high-priority jobs or handling tasks specific to certain business events), lazy-loading these handlers on-demand can lead to more efficient resource utilization.

In systems with numerous scheduled tasks, some tasks might be relevant only at specific times or under certain conditions. For example, a task might be necessary only at the end of a financial quarter or for generating reports specific to certain user segments. Lazy-loading these task handlers ensures that system resources are not tied up unnecessarily.

While lazy-loading adds some complexity, its benefits in terms of resource optimization and flexibility can be significant, especially in large applications with conditionally required functionalities like queue systems and scheduled tasks. As always, the key is to carefully evaluate whether the benefits outweigh the added complexity in your specific context.

If you have a keen interest in this topic, consider enrolling in the official course where there is an in-depth analysis of the advantages of lazy loading: https://learn.nestjs.com/p/advanced-concepts .

You can also reach this post on my medium.com where I share my solutions to the problems I encountered during my software engineer career.

If you’ve enjoyed reading my content and would like to support me, I kindly ask for a small donation of a cup of coffee. Your generosity would be greatly appreciated and will help me to continue creating meaningful and informative content. (donate a cup of coffee)

If you have additional questions, feel free to contact me on LinkedIn.

--

--

A full-stack developer living in Europe with expertise in Typescript, Javascript, PHP, Go. Experienced in system architecture, security, and UI design.