NestJS v9 is now available !

Kamil Mysliwiec | Trilon Consulting
Kamil Mysliwiec

Today I am thrilled to announce the official release of NestJS 9. This is a major release spanning the entire platform, including the framework, improvements to the @nestjs/swagger package, CLI, and updates to the documentation.

In case you're not familiar with NestJS, it is a TypeScript Node.js framework that helps you build enterprise-grade efficient and scalable Node.js applications.

Cat in Kyiv. Photo by Valdemar Kostenko

Let's dive right in!

What's new in version 9?

This release brings lots of great features and long-awaited improvements. There are far too many to list here, but let’s take a high-level look at some of the most exciting ones.

REPL (read–eval–print loop)

REPL is a simple interactive environment that takes single user inputs, executes them, and returns the result to the user. The REPL feature lets you inspect your dependency graph and call methods on your providers (and controllers) directly from your terminal.

Nest's REPL feature was first announced in May 2022 and since then it received a bunch of improvements & updates.

To run your NestJS application in REPL mode, create a new repl.ts file (alongside the existing main.ts file) and add the following code inside:

import { repl } from '@nestjs/core';
import { AppModule } from './app.module';

async function bootstrap() {
  await repl(AppModule);
}
bootstrap();

Now in your terminal, start the REPL with the following command:

$ npm run start -- --entryFile repl
## OR yarn start --entryFile repl

Hint The built-in NestJS REPL comes with a few native functions that are globally available when you start REPL. You can call help() to list them out.

Once it's up and running, you should see the following message in your console:

LOG [NestFactory] Starting Nest application...
LOG [InstanceLoader] AppModule dependencies initialized
LOG REPL initialized

And now you can start interacting with your dependencies graph. For instance, you can retrieve an AppService (we are using the starter project as an example here) and call the getHello() method:

> get(AppService).getHello()
'Hello World!'

You can execute any JavaScript code from within your terminal, for example, assign an instance of the AppController to a local variable, and use await to call an asynchronous method:

> appController = get(AppController)
AppController { appService: AppService {} }
> await appController.getHello()
'Hello World!'

To display all public methods available on a given provider or controller, use the methods() function, as follows:

> methods(AppController)

Methods:
 ◻ getHello

To print all registered modules as a list together with their controllers and providers, use debug().

> debug()

AppModule:
 - controllers:
  ◻ AppController
 - providers:
  ◻ AppService

Quick demo:

REPL example

Configurable module builder

If you have have ever manually created dynamic modules that are highly configurable that expose async methods, registerAsync, forRootAsync, etc., you know it can be quite complicated and require quite a bit of boilerplate code.

We aim to simplify this in Nest v9, which will provide a ConfigurableModuleBuilder class that facilitates this process and lets you construct a module "blueprint" - in just a few lines of code.

For demonstration purposes, let's create a configurable HttpClientModule using the new ConfigurableModuleBuilder. Before we start, let's make sure we declare a dedicated interface that represents what options our HttpClientModule takes in.

export interface HttpClientModuleOptions {
  baseUrl: string;
}

With this in place, create a new dedicated file named http-client.module-definition.ts. In this file, let's utilize the ConfigurableModuleBuilder to construct HttpClientModule definition.

import { ConfigurableModuleBuilder } from '@nestjs/common';
import { HttpClientModuleOptions } from './interfaces';

export const { ConfigurableModuleClass, MODULE_OPTIONS_TOKEN } =
  new ConfigurableModuleBuilder<HttpClientModuleOptions>().build();

And finally, let's define the actual http-client.module.ts module file and leverage the auto-generated ConfigurableModuleClass:

import { Module } from '@nestjs/common';
import { ConfigurableModuleClass } from './http-client.module-definition';

@Module({})
export class HttpClientModule extends ConfigurableModuleClass {}

Extending the ConfigurableModuleClass means that HttpClientModule provides now both register (for static use-cases) and registerAsync (which allows consumers asynchronously configure that module) methods.

@Module({
  imports: [
    HttpClientModule.register({ baseUrl: 'https://trilon.io' }),
    // or alternatively:
    // HttpClientModule.registerAsync({
    //   useFactory: () => {
    //     return {
    //       baseUrl: 'https://trilon.io',
    //     }
    //   },
    //   inject: [...any extra dependencies...]
    // }),
  ],
})
export class AppModule {}

You also have the ability to inject a configuration object using the @Inject(MODULE_OPTIONS_TOKEN) construction. Read more on this feature in the documentation.

Durable providers

Request-scoped providers may sometimes lead to increased latency since having at least 1 request-scoped provider (injected into the controller instance, or deeper - injected into one of its providers) makes the controller request-scoped as well. That means, it must be recreated (instantiated) per each individual request (and garbage collected afterwards).

HINT For instance, for 30k requests in parallel, there will be 30k ephemeral instances of the controller (and its request-scoped providers).

Having a common provider that most providers depend on (e.g., database connection), automatically converts all those providers to request-scoped providers as well. This can pose a challenge in multi-tenant applications, especially for those that have a central request-scoped "data source" provider that grabs headers/token from the request object and based on their values, retrieves the corresponding database connection/schema (specific to that tenant).

For instance, let's say you have an application alternately used by 10 different customers. Each customer has its own dedicated data source, and you want to make sure customer A will never be able to reach customer's B database. One way to achieve this could be to declare a request-scoped "data source" provider that - based on the request object - determines what's the "current customer" and retrieves its corresponding database. But, a major downside to this approach is that since most likely a large chunk of your application' components rely on the "data source" provider, they will implicitly become "request-scoped", and therefore you will undoubtedly see an impact in your apps performance.

But what if we had a better solution?

Since we only have 10 customers, couldn't we have 10 individual DI sub-trees per customer (instead of recreating each tree per request)?

If your providers don't rely on any property that's truly unique for each consecutive request (e.g., request UUID) but instead there are some specific attributes that let us aggregate them, then we have no reason to recreate the DI sub-tree on every incoming request.

This is exactly where durable providers come in handy!

Before we start flagging providers as durable, we must first register a strategy that:

  • instructs Nest what those "common request attributes" are
  • provides logic that groups requests, and associates them with their corresponding DI sub-trees.
import {
  HostComponentInfo,
  ContextId,
  ContextIdFactory,
  ContextIdStrategy,
} from '@nestjs/core';
import { Request } from 'express';

// A collection of context identifiers representing separate DI sub-trees per tenant
const tenants = new Map<string, ContextId>();

export class AggregateByTenantContextIdStrategy implements ContextIdStrategy {
  attach(contextId: ContextId, request: Request) {
    const tenantId = request.headers['x-tenant-id'] as string;
    let tenantSubTreeId: ContextId;
    if (tenants.has(tenantId)) {
      tenantSubTreeId = tenants.get(tenantId);
    } else {
      // Construct a new context id (think of a root node)
      tenantSubTreeId = ContextIdFactory.create();
      tenants.set(tenantId, tenantSubTreeId);
    }
    // If tree is not durable, return the original "contextId" object
    return (info: HostComponentInfo) =>
      info.isTreeDurable ? tenantSubTreeId : contextId;
  }
}

Hint Similar to the request scope, durability bubbles up the injection chain. That means if A depends on B which is flagged as durable, A implicitly becomes durable too (unless durable is explicitly set to false for A provider).

Warning Note this strategy is not ideal for applications operating with a large number of tenants.

With this strategy in place, you can register it somewhere in your code (as it applies globally anyway), so for example, we could place it in the main.ts file:

ContextIdFactory.apply(new AggregateByTenantContextIdStrategy());

As long as the registration occurs before any request hits your application, everything will work as intended.

Lastly, to turn a regular provider into a durable provider, simply set the durable flag to true:

import { Injectable, Scope } from '@nestjs/common';

@Injectable({ scope: Scope.REQUEST, durable: true })
export class CatsService {}

Similarly, for custom providers, set the durable property in the long-hand form for a provider registration:

{
  provide: 'CONNECTION_POOL',
  useFactory: () => { ... },
  scope: Scope.REQUEST,
  durable: true, // <-- here
}

Redis transporter

In v9, the Redis transport strategy will no longer use redis package under the hood but instead was migrated to leverage ioredis.

Upgraded dependencies

Nest v9 brings support for Fastify v4 and drops support for Node.js v10. Make sure to use at least the latest LTS version!

Migration from Nest v8

In order to migrate your existing project, follow the guidelines that are available here. Also, make sure to get rid of all deprecation messages that may appear in your console.

Enjoy v9!

We're excited to see how you use the latest version of NestJS.

Make sure to follow Nest on Twitter @nestframework to stay up to date with all the latest announcements!


Official NestJS Courses 📚

Looking to level-up your NestJS and Node.js ecosystem skills?

🚀 The NestJS Fundamentals Course is now LIVE and 25% off for a limited time!

In this extensive workshop-style course, we'll incrementally build a NestJS application together.

We'll learn about all of the fundamental building blocks, interact with both SQL & NoSql databases, use modern Node.js application techniques, unit / e2e testing, tips, tricks, and overall best-practices!


Enterprise Consulting & Support

Our official NestJS Enterprise Consulting includes a broad range of services to empower your team.

We work alongside you to meet your deadlines while avoiding costly tech debt. Challenging issue? We've got you covered.

  • Providing technical guidance & architectural reviews
  • Mentoring team members
  • Addressing security & performance concerns
  • Performing in-depth code reviews
  • Long-term support (LTS) & upgrade assistance

Our goal is to help you get to market faster. Nest core team members will help you utilize best practices and choose the right strategy for unique goals. You can learn more on the enterprise.nestjs.com.


Support the NestJS Project

Nest is an MIT-licensed open source project with its ongoing development made possible thanks to the support by the community and our sponsors. Thank you!

This framework is a result of the long days, sleepless nights, and busy weekends. And we fully rely on the goodness ❤️ of the people. If you want to join them, you can read more here.

T-Shirts and hoodies

If you enjoy using NestJS and you would like to donate to our Open Source work (have your cake 🍰 and eat it), we just launched the official NestJS store 👕 where you can buy hoodies, T-shirts, and accessories!


Thank you

To all backers, sponsors, contributors, and community, thank you once again! This product is for you. And this is only the beginning of the long 🚀 story.

Become a Backer or Sponsor to Nest by donating to our open collective. ❤

#NestJS
#NodeJS

Share this Post!

📬 Trilon Newsletter

Stay up to date with all the latest Articles & News!

More from the Trilon Blog .

Thiago Martins | Trilon Consulting
Thiago Martins

Advanced Testing Strategies with Mocks

Creating unit tests requires mocks or stubs to serve as proxy for some dependencies. In some cases, these dependencies are not so easily replaceable because they have many methods and/or objects as parameters. Here, we'll learn some strategies to easily mock them.

Read More
Maciej Sikorski | Trilon Consulting
Maciej Sikorski

Dependency Inversion Principle

This principle from SOLID is the most dependent on our Dependency Injection system. Let's look at how it works with NestJS!

Read More
Manuel Herrera | Trilon Consulting
Manuel Herrera

What is the NestJS Runtime

Let's look at how our NestJS applications tie everything together under the hood, and understand some common misconceptions with dependency injection, scopes, and much more.

Read More

What we do at Trilon .

At Trilon, our goal is to help elevate teams - giving them the push they need to truly succeed in today's ever-changing tech world.

Trilon - Consulting

Consulting .

Let us help take your Application to the next level - planning the next big steps, reviewing architecture, and brainstorming with the team to ensure you achieve your most ambitious goals!

Trilon - Development and Team Augmentation

Development .

Trilon can become part of your development process, making sure that you're building enterprise-grade, scalable applications with best-practices in mind, all while getting things done better and faster!

Trilon - Workshops on NestJS, Node, and other modern JavaScript topics

Workshops .

Have a Trilon team member come to YOU! Get your team up to speed with guided workshops on a huge variety of topics. Modern NodeJS (or NestJS) development, JavaScript frameworks, Reactive Programming, or anything in between! We've got you covered.

Trilon - Open-source contributors

Open-source .

We love open-source because we love giving back to the community! We help maintain & contribute to some of the largest open-source projects, and hope to always share our knowledge with the world!

Explore more

Write us a message .

Let's talk about how Trilon can help your next project get to the next level.

Rather send us an email? Write to:

hello@trilon.io
© 2019-2022 Trilon.