Microservice for handling notifications to users through real time notifications, email, or SMS. This microservice uses the loopback4-notifications module to publish/send the notifications.
npm i @sourceloop/notification-service
-
Create a new Loopback4 Application (If you don't have one already)
lb4 testapp
-
Install the notification service
npm i @sourceloop/notification-service
-
Install the loopback4-notifications module -
npm i loopback4-notifications
-
Set the environment variables.
-
Run the migrations.
-
Add the
NotificationServiceComponent
to your Loopback4 Application (inapplication.ts
).// add Component for AuthenticationService this.component(NotificationServiceComponent);
-
Set up a Loopback4 Datasource with
dataSourceName
property set toNotifDbSourceName
. You can see an example datasource here. -
Email Notifications with Amazon SES -
- Bind the SES Config to the
SESBindings.Config
key -
this.bind(SESBindings.Config).to({ accessKeyId: process.env.SES_ACCESS_KEY_ID, secretAccessKey: process.env.SES_SECRET_ACCESS_KEY, region: process.env.SES_REGION, });
- Implement an SES Provider(refer this) or you can import the default SES provider from the loopback4-notifications module and bind it to the
NotificationBindings.EmailProvider
key as described here.
- Bind the SES Config to the
-
Email Notifications with Nodemailer -
- Bind the Nodemailer Config to the
NodemailerBindings.Config
key -
this.bind(NodemailerBindings.Config).to({ pool: true, maxConnections: 100, url: '', host: 'smtp.example.com', port: 80, secure: false, auth: { user: 'username', pass: 'password', }, tls: { rejectUnauthorized: true, }, });
- Implement a Nodemailer Provider(refer this) or import the default Nodemailer provider from the loopback4-notifications module and bind it to the
NotificationBindings.EmailProvider
key as described here,
- Bind the Nodemailer Config to the
-
SMS Notification with Amazon SNS -
- Bind the SNS Config to the
SNSBindings.Config
key -
this.bind(SNSBindings.Config).to({ accessKeyId: process.env.SNS_ACCESS_KEY_ID, secretAccessKey: process.env.SNS_SECRET_ACCESS_KEY, region: process.env.SNS_REGION, });
- Implement an SnsProvider(refer this) or import the default SNS provider from the loopback4-notifications module and bind it to the
NotificationBindings.SMSProvider
key -
import { NotificationBindings, SnsProvider // or your own provider } from 'loopback4-notifications'; ... this.bind(NotificationBindings.SMSProvider).toProvider(SnsProvider); ...
- Bind the SNS Config to the
-
Push Notifications with Pubnub -
- Bind the Pubnub Config to the
PubNubProvider.Config
key -
this.bind(PubnubBindings.Config).to({ subscribeKey: process.env.PUBNUB_SUBSCRIBE_KEY, publishKey: process.env.PUBNUB_PUBLISH_KEY, secretKey: process.env.PUBNUB_SECRET_KEY, ssl: true, logVerbosity: true, uuid: 'my-app', cipherKey: process.env.PUBNUB_CIPHER_KEY, apns2Env: 'production', apns2BundleId: 'com.app.myapp', });
- Implement a Pubnub Provider(refer this) or import the default Pubnub provider from the loopback4-notifications module and bind it to the
NotificationBindings.PushProvider
key -
import { NotificationBindings, PubNubProvider //or your own provider } from 'loopback4-notifications'; ... this.bind(NotificationBindings.PushProvider).toProvider(PubNubProvider); ...
- Bind the Pubnub Config to the
-
Push Notifications with Socket.io -
- Bind the Socket.io Config to the
SocketBindings.Config
key -
this.bind(SocketBindings.Config).to({ url: process.env.SOCKETIO_SERVER_URL, });
- Implement a SocketIO Provider(refer this) or import the default Socket.io provider from the loopback4-notifications module and bind it to the
NotificationBindings.PushProvider
key -
import { NotificationBindings, SocketIOProvider // or your own provider } from 'loopback4-notifications'; ... this.bind(NotificationBindings.PushProvider).toProvider(SocketIOProvider); ...
- Bind the Socket.io Config to the
-
Start the application
npm start
interface SESMessage {
receiver: {
to: {
id: string; //Email address
name?: string;
}[];
};
subject: string;
body: string;
sentDate: Date;
type: 1; //Email
options?: {
fromEmail: string; // We do not support attachments with SES Provider yet.
};
}
interface NodemailerMessage {
receiver: {
to: {
id: string; //Email address
name?: string;
}[];
};
subject: string;
body: string;
sentDate: Date;
type: 1; //Email
options?: {
from: string;
subject?: string;
text?: string;
html?: string;
attachments?: {
filename?: string | false;
cid?: string;
encoding?: string;
contentType?: string;
contentTransferEncoding?: '7bit' | 'base64' | 'quoted-printable' | false;
contentDisposition?: 'attachment' | 'inline';
headers?: Headers;
raw?:
| string
| Buffer
| Readable
| {
content?: string | Buffer | Readable;
path?: string | Url;
};
}[];
};
}
interface SMSMessage {
receiver: {
to: {
id: string; // TopicArn or PhoneNumber
name?: string;
}[];
};
subject: undefined;
body: string;
sentDate: Date;
type: 2; //SMS
options?: {
messageType: any;
};
}
interface PubNubMessage {
receiver: {
to: {
type: 0; //Channel Type
id: string; //Channel identifier
name?: string;
}[];
};
subject: string;
body: string;
sentDate: Date;
type: 0; //Push Notification
options?: {
sound: string;
};
}
interface SocketMessage {
receiver: {
to: {
type: 0; //Channel Type
id: string; //Channel identifier
name?: string;
}[];
};
subject: string;
body: string;
sentDate: Date;
type: 0; //Push Notification
options?: {
path?: string;
};
}
Name | Required | Default Value | Description |
---|---|---|---|
NODE_ENV |
Y | Node environment value, i.e. dev , test , prod |
|
LOG_LEVEL |
Y | Log level value, i.e. error , warn , info , verbose , debug |
|
DB_HOST |
Y | Hostname for the database server. | |
DB_PORT |
Y | Port for the database server. | |
DB_USER |
Y | User for the database. | |
DB_PASSWORD |
Y | Password for the database user. | |
DB_DATABASE |
Y | Database to connect to on the database server. | |
DB_SCHEMA |
Y | Database schema used for the data source. In PostgreSQL, this will be public unless a schema is made explicitly for the service. |
|
JWT_SECRET |
Y | Symmetric signing key of the JWT token. | |
JWT_ISSUER |
Y | Issuer of the JWT token. |
Here is a sample Implementation DataSource
implementation using environment variables and PostgreSQL as the data source.
import {inject, lifeCycleObserver, LifeCycleObserver} from '@loopback/core';
import {juggler} from '@loopback/repository';
import {NotifDbSourceName} from '@sourceloop/notification-service';
const config = {
name: NotifDbSourceName,
connector: 'postgresql',
url: '',
host: process.env.DB_HOST,
port: process.env.DB_PORT,
user: process.env.DB_USER,
password: process.env.DB_PASSWORD,
database: process.env.DB_DATABASE,
schema: process.env.DB_SCHEMA,
};
@lifeCycleObserver('datasource')
export class NotificationDbDataSource
extends juggler.DataSource
implements LifeCycleObserver
{
static dataSourceName = NotifDbSourceName;
static readonly defaultConfig = config;
constructor(
// You need to set datasource configuration name as 'datasources.config.Notification' otherwise you might get Errors
@inject('datasources.config.Notification', {optional: true})
dsConfig: object = config,
) {
super(dsConfig);
}
}
Here is a sample implementation of how we can blacklist a user(s).
Create a new provider:
import {BindingScope, injectable, Provider} from '@loopback/core';
import {
INotificationFilterFunc,
Notification,
} from '@sourceloop/notification-service';
@injectable({scope: BindingScope.TRANSIENT})
export class NotificationfilterProvider
implements Provider<INotificationFilterFunc>
{
constructor() {}
value() {
return async (notif: Notification) => this.notificationFilterFunc(notif);
}
blacklistedUsers = ['id1', 'id2']; //ID's of blacklisted users
notificationFilterFunc(notif: Notification) {
const receivers = notif!.receiver.to;
const result = receivers.filter(receiver => {
return this.blacklistedUsers.indexOf(receiver.id) === -1;
});
notif.receiver.to = result;
return notif;
}
}
Add the following line to src/application.ts
file
this.bind(NotifServiceBindings.NotificationFilter).toProvider(
NotificationfilterProvider,
);
Note: One can modify the provider according to the requirements
The migrations required for this service are processed during the installation automatically if you set the NOTIF_MIGRATION
or SOURCELOOP_MIGRATION
env variable. The migrations use db-migrate
with db-migrate-pg
driver for migrations, so you will have to install these packages to use auto-migration. Please note that if you are using some pre-existing migrations or database, they may be effected. In such scenario, it is advised that you copy the migration files in your project root, using the NOTIF_MIGRATION_COPY
or SOURCELOOP_MIGRATION_COPY
env variables. You can customize or cherry-pick the migrations in the copied files according to your specific requirements and then apply them to the DB.
Authorization: Bearer where is a JWT token signed using JWT issuer and secret.
Content-Type: application/json
in the response and in request if the API method is NOT GET
{version}: Defines the API Version
200: Successful Response. Response body varies w.r.t API 401: Unauthorized: The JWT token is missing or invalid 403: Forbidden : Not allowed to execute the concerned API 404: Entity Not Found 400: Bad Request (Error message varies w.r.t API) 201: No content: Empty Response
Visit the OpenAPI spec docs