5 NestJS Techniques to Build Efficient and Maintainable Apps

5 NestJS Techniques to Build Efficient and Maintainable Apps

Sunny Sun Lv4

Learn five hidden gems in 5 minutes

NestJS is a complex framework that is continuously evolving. It’s a great framework with many out-of-the-box features. Some features are not well known and underutilized, but they are very powerful when used in the right place.

In this article, I’m going to walk through 5 hidden gems.

Versioning

Versioning is essential for web API development. We often need to create a new API version for a breaking change , while keeping the old version working.

Before NestJS 8, there was no out-of-box support for API versioning. Thus, you will need to implement the version manually.

Introduced in NestJS 8, 3 different types of versioning are supported.

  • URI versioning- The version will be passed within the URI of the request (default)
  • Header versioning- A custom request header will specify the version
  • Media Type versioning- The Accept header of the request will specify the version

Let’s use the default URI versioning as an example.

To enable URI Versioning, do the following:

1
2
3
app.enableVersioning({  
type: VersioningType.URI,
});

To apply URL versioning to the controller:

1
2
3
4
@Controller({  
version: '1',
})
export class CatsControllerV1 { ....

Or you can apply it to individual routes

1
2
3
4
5
@Version('2')  
@Get('cats')
findAllV2(): string {
return 'This action returns all cats for version 2';
}

Another useful feature is the Version “Neutral ”. You can set the controller to be version-neutral:

1
2
3
4
@Controller({  
version: VERSION_NEUTRAL,
})
export class CatsController {

If an incoming request does not contain a version at all, it will be mapped to the VERSION_NEUTRAL controller.

The new versioning feature in NestJS is simple and flexible. This is another good reason to use NestJS.

Use in-memory caching with a custom decorator

Caching is one of the most difficult problems in computer science. NestJs provides an out-of-box cache manager with an in-memory data store as default.

It’s straightforward to use. Firstly import the CacheModule.

1
imports: [CacheModule.register()]

Then we can inject it into a class using the CACHE_MANAGER token and start to use it.

1
2
3
constructor(@Inject(CACHE_MANAGER) private cacheManager: Cache) {}// To retrieve items from the cache  
await this.cacheManager.get('key');// To add an item to the cache
await this.cacheManager.set('key', 'value');

To implement caching in NestJS API, the code is typically like the following.

When the application grows, the code with the above pattern will be repeated everywhere.

How to reduce duplication? The answer is a custom decorator .

A decorator is a function that can extend the behavior of another function without modifying it. To create a decorator that can interact with the cache manager, we need to inject the cache manager into the decorator. The powerful NestJS DI makes it relatively easier.

Below is the implementation of a decorator that uses the in-memory cache manager.

The gist of the solution is:

  • target is the class that contains the decorator
  • descriptor is the reference to the class method being decorated
  • In line 12, we extract the target constructor name and method name to form a cacheKey, so it will be unique
  • ttlis the duration to invalidate the cache, we set the default to 10 seconds
  • Line 4 and line 10 are equivalent to calling the @Inject decorator in the constructor as follows:
    1
    constructor(@Inject(CACHE_MANAGER) private cacheManager: Cache) {}
    Usage of the decorator is as simple as:

It is so much cleaner now, and we can apply it to any service that needs to be cached.

NestJS Cron Job

Although NestJS is primarily designed for REST API, it also has a scheduling package. The package exposes an API that allows us to create cron jobs.

To get started with the NestJS cron job, we need to install the package first

1
2
$ npm install --save @nestjs/schedule   
$ npm install --save-dev @types/cron

You can create a cron job in two different ways:

  • create the cron job in a declarative way.

The @Cron decorator supports cron pattern .

1
2
3
4
@Cron('45 * * * * *')  
handleCron() {
this.logger.debug('Called when the current second is 45');
}

The handleCron method will be called every minute at the 45th second. You can also use the predefined cron expression to set up the job.

1
@Cron(CronExpression.EVERY_MINUTE)
  • dynamically create a new cron job.

Using the CronJob object from the cron package, you can create the cron job. Then you can use the SchedulerRegistry.addCronJob() method to add the job to the schedule registry.

1
2
3
4
5
6
7
addCronJob(name: string, seconds: string) {  
const job = new CronJob(`${seconds} * * * * *`, () => {
this.logger.warn(`time (${seconds}) for job ${name} to run!`);
});

this.schedulerRegistry.addCronJob(name, job);
job.start();

Check out the official documentation to find other options and features in the NestJS task scheduling package.

Validate input with pipes

Data validation is important for any Web API. In NestJS, we can use pipes to perform validation at run time. When a pipe is used for validation, it will return data unchanged if validation is successful, or an error will be thrown if the data is incorrect.

NestJS comes with built-in out-of-box pipes; one of them is ValidationPipe. The built-in ValidationPipe is based on the popular class-validator package. An example of usage is shown below:

  • @IsString(): whether an incoming value is a string,
  • @Length(10): verify whether the value has a minimum length of 10 characters. It will also return an error if the value is not a string.

When a request is received with an invalid property in the request body CreateCatDto, the app will automatically respond with a 400 Bad Request.

In the above example, the validation is applied with decorators. The same decorator can be used in different DTO classes across the whole NestJS App, making the validation logic easier to maintain.

There are a lot more decorators available from the built-in validation pipe. You can also create your own custom validation pipe on top of the class-transformer and class-validator packages.

Pipes in NestJS are flexible and powerful. It can be synchronous and asynchronous. It can also be applied at different levels of scope: parameter, method, controller, or global.

For more details on how to use NestJS pipes for validation, you can check out the official documentation .

Improve performance by switching underlying platform

By default, NestJS runs on top of Express. Compared with other API frameworks, its performance isn’t the best. For a normal app, the difference in performance won’t be noticed.

But if you are working on an App where very fast performance is the top priority, you can migrate your NestJS App to use Fastify . Fastify is much faster than Express, approximately two times quicker.

The migration from Express to Fastify is relatively easy, as NestJS provides framework independence based on adapter patterns. In other words, the design of NestJS makes Express and Fastify interchangeable.

To use Fastify, we need to install the package

1
npm i --save @nestjs/platform-fastify

Then, we can configure the Fastify platform in main.ts.

1
2
3
4
5
6
7
async function bootstrap() {  
const app = await NestFactory.create<NestFastifyApplication>(
AppModule,
new FastifyAdapter()
);
await app.listen(3000);
}

Now Fastify is ready to go!

Please note that Fastify is used as the HTTP provider in NestJS. Thus, those packages dependent on Express must be replaced by Fastify equivalent packages.

In this article, we have explored 5 less well-known features and their use cases. I hope you learned a thing or two.

  • Title: 5 NestJS Techniques to Build Efficient and Maintainable Apps
  • Author: Sunny Sun
  • Created at : 2022-04-01 00:00:00
  • Updated at : 2024-08-16 19:46:17
  • Link: http://coffeethinkcode.com/2022/04/01/5-nestjs-techiques-to-build-efficient-and-maintainable-app/
  • License: This work is licensed under CC BY-NC-SA 4.0.