Skip to main content

Messaging

MessagingUtils sends templated messages over email, Slack, and SMS. The same template, rendered with Handlebars, can drive all three depending on what a given template defines. Unlike NotificationUtils and AlertUtils, it has a no-argument constructor and uses @Config/@Logger injection internally, so it's directly @Inject-able like any other service:

import {ObjectDecorators} from '@rapidrest/core';
import {MessagingUtils} from '@rapidrest/core';
const {Inject} = ObjectDecorators;

export class PetService {
@Inject(MessagingUtils)
private messaging?: MessagingUtils;
}

Configuring channels

Each channel is configured independently, and MessagingUtils only sets up the channels it finds configuration for. An unconfigured channel simply throws if you try to send through it, rather than failing at startup.

conf.defaults({
smtp_config: {
host: 'smtp.example.com',
port: 587,
secure: false,
auth: {user: 'apikey', pass: 'change-me'},
},
slack: [
{name: 'default', token: 'xoxb-...', signingSecret: 'change-me'},
],
twilio: {
accountSid: 'AC...',
token: 'change-me',
},
});
Config keyChannelNotes
smtp_configEmailA single SMTP config, sent via Nodemailer.
slackSlackAn array, you can register more than one Slack app/workspace.
twilioSMSA single Twilio account config.

@slack/bolt, nodemailer, and twilio are all lazily import()-ed only for the channels you actually configure, so you only need to install the ones you use.

Templates

Every message is sent by name, referencing a template defined under the templates config key:

conf.defaults({
templates: {
// Required: the default "from" address/number for email and SMS
from: {
email: 'noreply@example.com',
sms: '+15555550100',
},
'pet-created': {
enabled: true,
subject: 'A new pet was added: {{name}}',
html: '<p>{{name}} the {{species}} was just added to your account.</p>',
slack_channel: '#pets',
slack_text: 'New pet: {{name}} ({{species}})',
sms: 'New pet added: {{name}}',
},
},
});

Each field (subject, html/text, slack_text, sms) is compiled independently as its own Handlebars template the first time the template is used, and cached from then on. enabled: false makes a template a no-op without deleting its configuration, which is handy for turning a notification off temporarily.

Loading templates from a file

For anything longer than a one-liner, htmlPath/textPath load the template body from a file on disk instead of inlining it in config. The file's contents are read once, compiled, and cached the same way an inline html/text value would be:

conf.defaults({
templates: {
from: {email: 'noreply@example.com', sms: '+15555550100'},
'pet-created': {
enabled: true,
subject: 'A new pet was added: {{name}}',
htmlPath: './templates/pet-created.html',
slack_channel: '#pets',
slack_text: 'New pet: {{name}} ({{species}})',
},
},
});

htmlPath/textPath and their inline html/text counterparts are interchangeable per-template. Use whichever fits a given message. There's no file-based equivalent for subject, slack_text, or sms; those are always inline strings.

Sending messages

await this.messaging?.sendEmail('pet-created', {name: pet.name, species: pet.species});
await this.messaging?.sendSlack('pet-created', {name: pet.name, species: pet.species});
await this.messaging?.sendSMS('pet-created', {name: pet.name, species: pet.species});

All three methods take the template name and the variables to render it with, and return undefined (rather than throwing) if the template exists but is disabled or missing the fields that channel needs. They only throw if the channel itself isn't configured at all, or the named template doesn't exist.