Modules

A module is a class annotated with a @Module() decorator. The @Module() decorator provides metadata that Nest makes use of to organize the application structure.

Modules - 图1

Each application has at least one module, a root module. The root module is the starting point Nest uses to build the application graph - the internal data structure Nest uses to resolve module and provider relationships and dependencies. While very small applications may theoretically have just the root module, this is not the typical case. We want to emphasize that modules are strongly recommended as an effective way to organize your components. Thus, for most applications, the resulting architecture will employ multiple modules, each encapsulating a closely related set of capabilities.

The @Module() decorator takes a single object whose properties describe the module:

providers the providers that will be instantiated by the Nest injector and that may be shared at least across this module
controllers the set of controllers defined in this module which have to be instantiated
imports the list of imported modules that export the providers which are required in this module
exports the subset of providers that are provided by this module and should be available in other modules which import this module

The module encapsulates providers by default. This means that it’s impossible to inject providers that are neither directly part of the current module nor exported from the imported modules. Thus, you may consider the exported providers from a module as the module’s public interface, or API.

Feature modules

The CatsController and CatsService belong to the same application domain. As they are closely related, it makes sense to move them into a feature module. A feature module simply organizes code relevant for a specific feature, keeping code organized and establishing clear boundaries. This helps us manage complexity and develop with SOLID principles, especially as the size of the application and/or team grow.

To demonstrate this, we’ll create the CatsModule.

  1. @@filename(cats/cats.module)
  2. import { Module } from '@nestjs/common';
  3. import { CatsController } from './cats.controller';
  4. import { CatsService } from './cats.service';
  5. @Module({
  6. controllers: [CatsController],
  7. providers: [CatsService],
  8. })
  9. export class CatsModule {}

info Hint To create a module using the CLI, simply execute the $ nest g module cats command.

Above, we defined the CatsModule in the cats.module.ts file, and moved everything related to this module into the cats directory. The last thing we need to do is import this module into the root module (the AppModule, defined in the app.module.ts file).

  1. @@filename(app.module)
  2. import { Module } from '@nestjs/common';
  3. import { CatsModule } from './cats/cats.module';
  4. @Module({
  5. imports: [CatsModule],
  6. })
  7. export class AppModule {}

Here is how our directory structure looks now:

src
cats
dto
create-cat.dto.ts
interfaces
cat.interface.ts
cats.service.ts
cats.controller.ts
cats.module.ts
app.module.ts
main.ts

Shared modules

In Nest, modules are singletons by default, and thus you can share the same instance of any provider between multiple modules effortlessly.

Modules - 图2

Every module is automatically a shared module. Once created it can be reused by any module. Let’s imagine that we want to share an instance of the CatsService between several other modules. In order to do that, we first need to export the CatsService provider by adding it to the module’s exports array, as shown below:

  1. @@filename(cats.module)
  2. import { Module } from '@nestjs/common';
  3. import { CatsController } from './cats.controller';
  4. import { CatsService } from './cats.service';
  5. @Module({
  6. controllers: [CatsController],
  7. providers: [CatsService],
  8. exports: [CatsService]
  9. })
  10. export class CatsModule {}

Now any module that imports the CatsModule has access to the CatsService and will share the same instance with all other modules that import it as well.

Module re-exporting

As seen above, Modules can export their internal providers. In addition, they can re-export modules that they import. In the example below, the CommonModule is both imported into and exported from the CoreModule, making it available for other modules which import this one.

  1. @Module({
  2. imports: [CommonModule],
  3. exports: [CommonModule],
  4. })
  5. export class CoreModule {}

Dependency injection

A module class can inject providers as well (e.g., for configuration purposes):

  1. @@filename(cats.module)
  2. import { Module } from '@nestjs/common';
  3. import { CatsController } from './cats.controller';
  4. import { CatsService } from './cats.service';
  5. @Module({
  6. controllers: [CatsController],
  7. providers: [CatsService],
  8. })
  9. export class CatsModule {
  10. constructor(private readonly catsService: CatsService) {}
  11. }
  12. @@switch
  13. import { Module, Dependencies } from '@nestjs/common';
  14. import { CatsController } from './cats.controller';
  15. import { CatsService } from './cats.service';
  16. @Module({
  17. controllers: [CatsController],
  18. providers: [CatsService],
  19. })
  20. @Dependencies(CatsService)
  21. export class CatsModule {
  22. constructor(catsService) {
  23. this.catsService = catsService;
  24. }
  25. }

However, module classes themselves cannot be injected as providers due to circular dependency .

Global modules

If you have to import the same set of modules everywhere, it can get tedious. Unlike in Nest, Angular providers are registered in the global scope. Once defined, they’re available everywhere. Nest, however, encapsulates providers inside the module scope. You aren’t able to use a module’s providers elsewhere without first importing the encapsulating module.

When you want to provide a set of providers which should be available everywhere out-of-the-box (e.g., helpers, database connections, etc.), make the module global with the @Global() decorator.

  1. import { Module, Global } from '@nestjs/common';
  2. import { CatsController } from './cats.controller';
  3. import { CatsService } from './cats.service';
  4. @Global()
  5. @Module({
  6. controllers: [CatsController],
  7. providers: [CatsService],
  8. exports: [CatsService],
  9. })
  10. export class CatsModule {}

The @Global() decorator makes the module global-scoped. Global modules should be registered only once, generally by the root or core module. In the above example, the CatsService provider will be ubiquitous, and modules that wish to inject the service will not need to import the CatsModule in their imports array.

info Hint Making everything global is not a good design decision. Global modules are available to reduce the amount of necessary boilerplate. The imports array is generally the preferred way to make the module’s API available to consumers.

Dynamic modules

The Nest module system includes a powerful feature called dynamic modules. This feature enables you to easily create customizable modules that can register and configure providers dynamically. Dynamic modules are covered extensively here. In this chapter, we’ll give a brief overview to complete the introduction to modules.

Following is an example of a dynamic module definition for a DatabaseModule:

  1. @@filename()
  2. import { Module, DynamicModule } from '@nestjs/common';
  3. import { createDatabaseProviders } from './database.providers';
  4. import { Connection } from './connection.provider';
  5. @Module({
  6. providers: [Connection],
  7. })
  8. export class DatabaseModule {
  9. static forRoot(entities = [], options?): DynamicModule {
  10. const providers = createDatabaseProviders(options, entities);
  11. return {
  12. module: DatabaseModule,
  13. providers: providers,
  14. exports: providers,
  15. };
  16. }
  17. }
  18. @@switch
  19. import { Module } from '@nestjs/common';
  20. import { createDatabaseProviders } from './database.providers';
  21. import { Connection } from './connection.provider';
  22. @Module({
  23. providers: [Connection],
  24. })
  25. export class DatabaseModule {
  26. static forRoot(entities = [], options?) {
  27. const providers = createDatabaseProviders(options, entities);
  28. return {
  29. module: DatabaseModule,
  30. providers: providers,
  31. exports: providers,
  32. };
  33. }
  34. }

info Hint The forRoot() method may return a dynamic module either synchronously or asynchronously (i.e., via a Promise).

This module defines the Connection provider by default (in the @Module() decorator metadata), but additionally - depending on the entities and options objects passed into the forRoot() method - exposes a collection of providers, for example, repositories. Note that the properties returned by the dynamic module extend (rather than override) the base module metadata defined in the @Module() decorator. That’s how both the statically declared Connection provider and the dynamically generated repository providers are exported from the module.

Once defined as above, the DatabaseModule can be imported and configured in the following manner:

  1. import { Module } from '@nestjs/common';
  2. import { DatabaseModule } from './database/database.module';
  3. import { User } from './users/entities/user.entity';
  4. @Module({
  5. imports: [DatabaseModule.forRoot([User])],
  6. })
  7. export class AppModule {}

If you want to in turn re-export a dynamic module, you can omit the forRoot() method call in the exports array:

  1. import { Module } from '@nestjs/common';
  2. import { DatabaseModule } from './database/database.module';
  3. import { User } from './users/entities/user.entity';
  4. @Module({
  5. imports: [DatabaseModule.forRoot([User])],
  6. exports: [DatabaseModule],
  7. })
  8. export class AppModule {}

The Dynamic modules chapter covers this topic in greater detail, and includes a working example.