NestboltNestbolt

@nestbolt/notifications

Configuration

Configure @nestbolt/notifications with forRoot() and forRootAsync() -- database channel, mail transport, default sender, and custom channels.

The NotificationModule supports two configuration methods: forRoot() for static configuration and forRootAsync() for dynamic configuration using dependency injection. Both register the module globally, so you only need to import it once in your root module.

Static Configuration (forRoot)

Use forRoot() when your configuration values are known at compile time:

import { Module } from "@nestjs/common";
import { NotificationModule } from "@nestbolt/notifications";

@Module({
  imports: [
    NotificationModule.forRoot({
      channels: {
        database: true,
        mail: {
          transport: {
            host: "smtp.example.com",
            port: 587,
            secure: false,
            auth: {
              user: "user@example.com",
              pass: "password",
            },
          },
          defaults: {
            from: "noreply@example.com",
          },
        },
      },
    }),
  ],
})
export class AppModule {}

Async Configuration (forRootAsync)

Use forRootAsync() when configuration values come from environment variables, a config service, or any other async source:

import { Module } from "@nestjs/common";
import { ConfigModule, ConfigService } from "@nestjs/config";
import { NotificationModule } from "@nestbolt/notifications";

@Module({
  imports: [
    ConfigModule.forRoot(),
    NotificationModule.forRootAsync({
      imports: [ConfigModule],
      inject: [ConfigService],
      useFactory: (config: ConfigService) => ({
        channels: {
          database: true,
          mail: {
            transport: {
              host: config.get<string>("SMTP_HOST"),
              port: config.get<number>("SMTP_PORT"),
              secure: config.get<boolean>("SMTP_SECURE", false),
              auth: {
                user: config.get<string>("SMTP_USER"),
                pass: config.get<string>("SMTP_PASS"),
              },
            },
            defaults: {
              from: config.get<string>("MAIL_FROM", "noreply@example.com"),
            },
          },
        },
      }),
    }),
  ],
})
export class AppModule {}

The forRootAsync() method accepts the following options:

OptionTypeDescription
importsany[]Modules to import (e.g., ConfigModule).
injectany[]Providers to inject into useFactory.
useFactory(...args) => NotificationModuleOptionsFactory function that returns the module options.
customChannelsRecord<string, Type<NotificationChannel>>Custom channel classes to register (see below).

The useFactory function can also return a Promise<NotificationModuleOptions> for async initialization.

NotificationModuleOptions

The options object passed to forRoot() or returned from useFactory has the following shape:

interface NotificationModuleOptions {
  channels?: {
    database?: boolean;
    mail?: MailChannelOptions;
    custom?: Record<string, Type<NotificationChannel>>;
  };
}

channels.database

TypeDefaultDescription
booleantrueEnable or disable the database channel.

The database channel is enabled by default. It stores notification payloads in the notifications table via TypeORM. Set it to false to disable it entirely:

NotificationModule.forRoot({
  channels: {
    database: false, // Disable database notifications
    mail: { /* ... */ },
  },
});

channels.mail

TypeDefaultDescription
MailChannelOptionsundefinedMail channel configuration.

If not provided, the mail channel is not registered. Attempting to send a notification via the "mail" channel without configuring it will throw a ChannelNotFoundException.

The MailChannelOptions interface:

interface MailChannelOptions {
  transport: {
    host: string;
    port: number;
    secure?: boolean;
    auth?: {
      user: string;
      pass: string;
    };
  };
  defaults?: {
    from?: string;
  };
}

transport

The transport object is passed directly to nodemailer.createTransport(). It supports any transport configuration that nodemailer accepts.

PropertyTypeDescription
hoststringSMTP server hostname.
portnumberSMTP server port (typically 25, 465, or 587).
securebooleanUse TLS. Set true for port 465, false for STARTTLS on 587.
authobjectAuthentication credentials.
auth.userstringSMTP username.
auth.passstringSMTP password.

defaults

PropertyTypeDescription
fromstringDefault sender address used when a notification does not specify one via MailMessage.from().

channels.custom

TypeDefaultDescription
Record<string, Type<NotificationChannel>>undefinedA map of channel names to channel classes.

Register custom channel implementations. Each key is the channel name (used in via()), and each value is the class (not an instance) of a provider implementing NotificationChannel.

NotificationModule.forRoot({
  channels: {
    database: true,
    custom: {
      slack: SlackChannel,
      sms: TwilioSmsChannel,
      webhook: WebhookChannel,
    },
  },
});

See Custom Channels for details on implementing custom channels.

Registering Custom Channels with forRootAsync

When using forRootAsync(), custom channels are registered via the top-level customChannels option rather than inside useFactory. This is because custom channel classes need to be registered as NestJS providers before the factory runs:

import { SlackChannel } from "./channels/slack.channel";

NotificationModule.forRootAsync({
  imports: [ConfigModule],
  inject: [ConfigService],
  useFactory: (config: ConfigService) => ({
    channels: {
      database: true,
      mail: {
        transport: {
          host: config.get("SMTP_HOST"),
          port: config.get("SMTP_PORT"),
          auth: {
            user: config.get("SMTP_USER"),
            pass: config.get("SMTP_PASS"),
          },
        },
      },
    },
  }),
  customChannels: {
    slack: SlackChannel,
  },
});

Database-Only Configuration

The simplest possible configuration enables only the database channel:

NotificationModule.forRoot({
  channels: {
    database: true,
  },
});

Since database: true is the default, you can also call forRoot() with no arguments:

NotificationModule.forRoot();

Mail-Only Configuration

If you only need email notifications and do not want database storage:

NotificationModule.forRoot({
  channels: {
    database: false,
    mail: {
      transport: {
        host: "smtp.example.com",
        port: 587,
        auth: { user: "user", pass: "pass" },
      },
      defaults: { from: "noreply@example.com" },
    },
  },
});

Global Registration

Both forRoot() and forRootAsync() register the module as global. This means NotificationService and other exported providers are available in every module of your application without needing to re-import NotificationModule.

The following are exported from the module and available for injection:

ProviderDescription
NotificationServiceThe main service for sending and querying notifications.
ChannelManagerThe internal channel registry (rarely needed directly).
NOTIFICATION_OPTIONSThe raw options object (injection token).