Modular Deployments with NestJS

Probably whenever you had the necessity of implementing different processes (CronJobs, HTTP Request handling, Queue Listeners, etc) in your backend services, you’ve had the temptation of writing different microservices to run them so you spread the load and the responsibility among those running processes. This can be a good approach, but I will share with you how you can define and configure different deployments for your NestJS application without having to manage different codebases, let’s get to it.

We will use the NestJS CLI to create a new NestJS project.

$ npm i -g @nestjs/cli
$ nest new project-name

Then, we will see our default starter project created within the project-name folder. In the app.module.ts file you should see something like this:

import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';

@Module({
imports: [],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}

We will remove this annotation definition of a module and we will create a function under the AppModule class that receives as parameter the type of deployment we want to have, this will allow us to configure which imports, controllers and providers we want to embbed into our application. It will look something like this:

/* 
Here you can add as much types as you need,
In this case we will go with the following as
they are the more common.
*/
export enum ServerType {
CRON = 'cron',
WEB_API = 'webapi',
QUEUE_HANDLER = 'queuehandler',
ALL = 'all'
}

@Module({})
export class AppModule {
static register(mode: ServerType): DynamicModule {
const defaultImports = [];
const apiControllers = [AppController];
const defaultProviders: Provider[] = [AppService];
return {
module: AppModule,
imports: defaultImports,
controllers: apiControllers,
providers: defaultProviders
};
}
}

In the main.ts file you will see something like the following:

async function bootstrap() {
const app = await NestFactory.create(AppModule);
await app.listen(3000);
}
bootstrap();

Instead of passing AppModule class to the NestFactory.create function, we pass the result of invoking the register() function that we created earlier in the AppModule class:

async function bootstrap() {
/*
Check for a defined type in an environment variable. We default it to ALL
*/
let type: ServerType = process.env.SERVER_TYPE as ServerType;
if (!type) {
type = ServerType.ALL;
}
const app = await NestFactory.create(AppModule.register(type));
await app.listen(3000);
}
bootstrap();

In this case we are reading the type from an environment variable, but you can also read it from the command line or from any other way you use on your deployments. The beauty here is that you just need to pass a single parameter and you can customize your deployments. This approach of creating a function and returning a DynamicModule type will behave exactly the same as the first annotation approach, the difference now is that we have more flexibility into what we can register into the AppModule.

Now we can do some conditional logic around the imports, controllers and providers that we want to bundle depending on the ServerType, our AppModule will look now something like this:

export enum ServerType {
CRON = 'cron',
WEB_API = 'webapi',
QUEUE_HANDLER = 'queuehandler',
ALL = 'all'
}

@Module({})
export class AppModule {
static register(mode: ServerType): DynamicModule {
let apiControllers = [];
const defaultImports = [];
const defaultProviders: Provider[] = [AppService];
const exports = [];
if ([ServerType.CRON, ServerType.ALL].includes(mode)) {
// Add Nest.js scheduler module
defaultImports.push(ScheduleModule.forRoot());
// Custom BatchProcessor CronJob that executes some task
defaultProviders.push(BatchProcessor);
}

if ([ServerType.WEB_API, ServerType.ALL].includes(mode)) {
// Our HTTP handlers
apiControllers = [
AppController,
];
}
if ([ServerType.QUEUE_HANDLER, ServerType.ALL].includes(mode)) {
// A Custom listener for a queueing service
apiControllers.push(QueueHandler);
}
return {
module: AppModule,
imports: defaultImports,
controllers: apiControllers,
providers: defaultProviders,
exports: exports,
};
}
}

This way you can configure your deployments in a way that your application only loads exactly what it needs and you can scale your different features independently. For example, if your reading queue experiences some overflow of messages, you can scale up only the Queue Handler deployment without scaling the Web API deployment or the CronJobs deployments.

Here is a link of the complete example so you can reference easily

https://github.com/jalvar53/nestjs-modular-deployment

Hopefully this has been helpful for you, keep on hacking!

Build Apps with reusable components, just like Lego

Bit’s open-source tool help 250,000+ devs to build apps with components.

Turn any UI, feature, or page into a reusable component — and share it across your applications. It’s easier to collaborate and build faster.

Learn more

Split apps into components to make app development easier, and enjoy the best experience for the workflows you want:

Micro-Frontends

Design System

Code-Sharing and reuse

Monorepo

Learn more


Modular Deployments with NestJS was originally published in Bits and Pieces on Medium, where people are continuing the conversation by highlighting and responding to this story.


This content originally appeared on Bits and Pieces - Medium and was authored by Jose Alvarez

Probably whenever you had the necessity of implementing different processes (CronJobs, HTTP Request handling, Queue Listeners, etc) in your backend services, you’ve had the temptation of writing different microservices to run them so you spread the load and the responsibility among those running processes. This can be a good approach, but I will share with you how you can define and configure different deployments for your NestJS application without having to manage different codebases, let’s get to it.

We will use the NestJS CLI to create a new NestJS project.

$ npm i -g @nestjs/cli
$ nest new project-name

Then, we will see our default starter project created within the project-name folder. In the app.module.ts file you should see something like this:

import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';

@Module({
imports: [],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}

We will remove this annotation definition of a module and we will create a function under the AppModule class that receives as parameter the type of deployment we want to have, this will allow us to configure which imports, controllers and providers we want to embbed into our application. It will look something like this:

/* 
Here you can add as much types as you need,
In this case we will go with the following as
they are the more common.
*/
export enum ServerType {
CRON = 'cron',
WEB_API = 'webapi',
QUEUE_HANDLER = 'queuehandler',
ALL = 'all'
}

@Module({})
export class AppModule {
static register(mode: ServerType): DynamicModule {
const defaultImports = [];
const apiControllers = [AppController];
const defaultProviders: Provider[] = [AppService];
return {
module: AppModule,
imports: defaultImports,
controllers: apiControllers,
providers: defaultProviders
};
}
}

In the main.ts file you will see something like the following:

async function bootstrap() {
const app = await NestFactory.create(AppModule);
await app.listen(3000);
}
bootstrap();

Instead of passing AppModule class to the NestFactory.create function, we pass the result of invoking the register() function that we created earlier in the AppModule class:

async function bootstrap() {
/*
Check for a defined type in an environment variable. We default it to ALL
*/
let type: ServerType = process.env.SERVER_TYPE as ServerType;
if (!type) {
type = ServerType.ALL;
}
const app = await NestFactory.create(AppModule.register(type));
await app.listen(3000);
}
bootstrap();

In this case we are reading the type from an environment variable, but you can also read it from the command line or from any other way you use on your deployments. The beauty here is that you just need to pass a single parameter and you can customize your deployments. This approach of creating a function and returning a DynamicModule type will behave exactly the same as the first annotation approach, the difference now is that we have more flexibility into what we can register into the AppModule.

Now we can do some conditional logic around the imports, controllers and providers that we want to bundle depending on the ServerType, our AppModule will look now something like this:

export enum ServerType {
CRON = 'cron',
WEB_API = 'webapi',
QUEUE_HANDLER = 'queuehandler',
ALL = 'all'
}

@Module({})
export class AppModule {
static register(mode: ServerType): DynamicModule {
let apiControllers = [];
const defaultImports = [];
const defaultProviders: Provider[] = [AppService];
const exports = [];
if ([ServerType.CRON, ServerType.ALL].includes(mode)) {
// Add Nest.js scheduler module
defaultImports.push(ScheduleModule.forRoot());
// Custom BatchProcessor CronJob that executes some task
defaultProviders.push(BatchProcessor);
}

if ([ServerType.WEB_API, ServerType.ALL].includes(mode)) {
// Our HTTP handlers
apiControllers = [
AppController,
];
}
if ([ServerType.QUEUE_HANDLER, ServerType.ALL].includes(mode)) {
// A Custom listener for a queueing service
apiControllers.push(QueueHandler);
}
return {
module: AppModule,
imports: defaultImports,
controllers: apiControllers,
providers: defaultProviders,
exports: exports,
};
}
}

This way you can configure your deployments in a way that your application only loads exactly what it needs and you can scale your different features independently. For example, if your reading queue experiences some overflow of messages, you can scale up only the Queue Handler deployment without scaling the Web API deployment or the CronJobs deployments.

Here is a link of the complete example so you can reference easily

https://github.com/jalvar53/nestjs-modular-deployment

Hopefully this has been helpful for you, keep on hacking!

Build Apps with reusable components, just like Lego

Bit’s open-source tool help 250,000+ devs to build apps with components.

Turn any UI, feature, or page into a reusable component — and share it across your applications. It’s easier to collaborate and build faster.

Learn more

Split apps into components to make app development easier, and enjoy the best experience for the workflows you want:

Micro-Frontends

Design System

Code-Sharing and reuse

Monorepo

Learn more


Modular Deployments with NestJS was originally published in Bits and Pieces on Medium, where people are continuing the conversation by highlighting and responding to this story.


This content originally appeared on Bits and Pieces - Medium and was authored by Jose Alvarez


Print Share Comment Cite Upload Translate Updates
APA

Jose Alvarez | Sciencx (2023-02-17T13:24:44+00:00) Modular Deployments with NestJS. Retrieved from https://www.scien.cx/2023/02/17/modular-deployments-with-nestjs/

MLA
" » Modular Deployments with NestJS." Jose Alvarez | Sciencx - Friday February 17, 2023, https://www.scien.cx/2023/02/17/modular-deployments-with-nestjs/
HARVARD
Jose Alvarez | Sciencx Friday February 17, 2023 » Modular Deployments with NestJS., viewed ,<https://www.scien.cx/2023/02/17/modular-deployments-with-nestjs/>
VANCOUVER
Jose Alvarez | Sciencx - » Modular Deployments with NestJS. [Internet]. [Accessed ]. Available from: https://www.scien.cx/2023/02/17/modular-deployments-with-nestjs/
CHICAGO
" » Modular Deployments with NestJS." Jose Alvarez | Sciencx - Accessed . https://www.scien.cx/2023/02/17/modular-deployments-with-nestjs/
IEEE
" » Modular Deployments with NestJS." Jose Alvarez | Sciencx [Online]. Available: https://www.scien.cx/2023/02/17/modular-deployments-with-nestjs/. [Accessed: ]
rf:citation
» Modular Deployments with NestJS | Jose Alvarez | Sciencx | https://www.scien.cx/2023/02/17/modular-deployments-with-nestjs/ |

Please log in to upload a file.




There are no updates yet.
Click the Upload button above to add an update.

You must be logged in to translate posts. Please log in or register.