Template - API
WelcomePrerequisitesNodeJS
  • Welcome!
    • About me
  • Prerequisites
    • Installation and Database Setup
    • Installing Node.js on the Server
  • NodeJS
    • 🆓Getting Started with the Free Template
    • 🎗️Premium Template - REST API
      • Structure
      • Middlewares and Guards
      • Core Modules
        • Prisma with NestJS
        • Swagger with NestJS
      • Dynamic Module
        • Create or Import Module
  • Modules
    • 🎗️Websockets
    • 🎗️Mailer
    • 🎗️Upload
Powered by GitBook
On this page
  • Prerequisites
  • Example Usage
  • Component Explanation
  1. NodeJS
  2. Premium Template - REST API
  3. Dynamic Module

Create or Import Module

You can either create a module by creating a folder and adding your classes following the NestJS conventions, or you can import a compatible module directly into the src/modules/ folder.


Prerequisites

There are a few prerequisites for your module to be recognized:

  1. The @DynamicModule({}) decorator must be added to the module.

  2. The .ts file must end with .module.ts.


Example Usage

When you move this module into a designated folder like src/modules, it can be automatically loaded by your dynamic loading service. The controller exposes two endpoints to test the API:

This module example can serve as a foundation in the documentation to explain how to create a simple module in NestJS while adhering to SOLID principles and the modular architecture of your API.


Component Explanation

MyCustomModule: The main module that encapsulates the controller and the service.

import { Module } from '@nestjs/common';
import { MyCustomService } from './services/my-custom.service';
import { MyCustomController } from './controllers/my-custom.controller';
import { DynamicModule } from '../../common/decorators/dynamic.module.decorator';

@DynamicModule({})
@Module({
  controllers: [MyCustomController],
  providers: [MyCustomService],
})
export class MyCustomModule {}

MyCustomController: Responsible for handling incoming HTTP requests. It provides two routes:

  • GET /mycustom to retrieve all entries.

  • POST /mycustom to create a new entry.

import { Controller, Get, Post, Body } from '@nestjs/common';
import { MyCustomService } from '../services/my-custom.service';
import { CreateMyCustomDto } from '../dto/create-my-custom.dto';

@Controller('mycustom')
export class MyCustomController {
  constructor(private readonly myCustomService: MyCustomService) {}

  @Get()
  findAll() {
    return this.myCustomService.findAll();
  }

  @Post()
  create(@Body() createMyCustomDto: CreateMyCustomDto) {
    return this.myCustomService.create(createMyCustomDto);
  }
}

MyCustomService: Handles the business logic related to "MyCustom" entities, storing data in memory and allowing creation and retrieval operations.

import { Injectable } from '@nestjs/common';
import { MyCustomServiceInterface } from '../interfaces/my-custom-service.interface';
import { CreateMyCustomDto } from '../dto/create-my-custom.dto';

@Injectable()
export class MyCustomService implements MyCustomServiceInterface {
  private items = [];

  findAll() {
    return this.items;
  }

  create(createMyCustomDto: CreateMyCustomDto) {
    const newItem = { id: Date.now(), ...createMyCustomDto };
    this.items.push(newItem);
    return newItem;
  }
}

CreateMyCustomDto: A Data Transfer Object (DTO) used to validate incoming data when creating a new "MyCustom" entry.

import { CreateMyCustomDto } from '../dto/create-my-custom.dto';

export interface MyCustomServiceInterface {
  findAll(): any[];
  create(createMyCustomDto: CreateMyCustomDto): any;
}

MyCustomServiceInterface: Defines the contracts the service must adhere to, ensuring compliance with best practices.

import { CreateMyCustomDto } from '../dto/create-my-custom.dto';

export interface MyCustomServiceInterface {
  findAll(): any[];
  create(createMyCustomDto: CreateMyCustomDto): any;
}

MyCustomModule Decorator: A custom decorator that can be used to add metadata to dynamic modules (optional but useful for automation or dynamic loading scenarios).

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

export const IS_MY_CUSTOM_MODULE = 'isMyCustomModule';

export const MyCustomModuleDecorator = () => SetMetadata(IS_MY_CUSTOM_MODULE, true);

MyCustomSubModule: A submodule loader is also available. It searches within your module path, for example src/modules/mycustommodule/, to see if a subModules folder exists. It scans this folder looking for the following prerequisites:

The submodule decorator must be added: @MYCUSTOMDECORATOR({}).

  1. The custom decorator is defined within a service who extends BaseSubModulesLoader:

import { Logger, Injectable, OnModuleInit } from '@nestjs/common';
import { BaseSubModulesLoader } from '../../../common/services/base-submodules-loader.service';
import { join } from 'path';

/**
 * DynamicSubModulesLoader Service
 * 
 * This service is responsible for dynamically loading NestJS modules from a specified directory.
 * It scans the directory for module folders, imports them, and verifies if they are marked as dynamic
 * using a custom decorator. Only modules marked as dynamic are loaded and returned for integration
 * into the NestJS application.
 */
@Injectable()
export class DynamicSubModulesLoader extends BaseSubModulesLoader {
  protected readonly logger = new Logger(this.constructor.name);
  protected metadataKey = 'MYCUSTOMDECORATOR';
  protected targetDirectory = join(__dirname, 'MY_PATH_TO_THE_SUBMODULE_DIRECTORY');
}
import { Module, OnModuleInit } from '@nestjs/common';

// Decorators
import { MYCUSTOMDECORATOR } from 'MY_PATH_TO_THE_SUBMODULE_DIRECTORY';
// Database
import { User } from './models/user.model'; // Path to the User model

@MYCUSTOMDECORATOR({})
@Module({
    imports: [],
})
// The UserModule encapsulates all functionality related to user, 
// such as routing, business logic, and database operations.
export class UserModule implements OnModuleInit{
    onModuleInit() {
        console.debug('[DynamicModule]:[Sub]:[ UserModule ] -> initialisé');
    }
}
  1. The submodule file must end with .module.ts.

The submodule functions as a fully independent module and can implement the same tools as a parent module. However, its loading depends on the parent module.

PreviousDynamic ModuleNextModules

Last updated 8 months ago

🎗️