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:
The
@DynamicModule({})
decorator must be added to the module.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({})
.
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é');
}
}
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.
Last updated