HackMD
    • Create new note
    • Create a note from template
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
      • Invitee
      • No invitee
    • Publish Note

      Publish Note

      Everyone on the web can find and read all notes of this public team.
      Once published, notes can be searched and viewed by anyone online.
      See published notes
      Please check the box to agree to the Community Guidelines.
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Engagement control
    • Transfer ownership
    • Delete this note
    • Save as template
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Versions and GitHub Sync Engagement control Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
Invitee
No invitee
Publish Note

Publish Note

Everyone on the web can find and read all notes of this public team.
Once published, notes can be searched and viewed by anyone online.
See published notes
Please check the box to agree to the Community Guidelines.
Engagement control
Commenting
Permission
Disabled Forbidden Owners Signed-in users Everyone
Enable
Permission
  • Forbidden
  • Owners
  • Signed-in users
  • Everyone
Suggest edit
Permission
Disabled Forbidden Owners Signed-in users Everyone
Enable
Permission
  • Forbidden
  • Owners
  • Signed-in users
Emoji Reply
Enable
Import from Dropbox Google Drive Gist Clipboard
   owned this note    owned this note      
Published Linked with GitHub
Subscribed
  • Any changes
    Be notified of any changes
  • Mention me
    Be notified of mention me
  • Unsubscribe
Subscribe
> Imagine having a whole abstraction capable of sending email, SMS, or notification to all your users in one place . > > Prerequisite : Knowledge of either Javascript, (ExpressJs or Sails Js) and NodeJs . Abstraction, What does it mean? [Abstraction](https://en.wikipedia.org/wiki/Abstraction_(computer_science)) according to computing is the process of generalising or hiding concrete details of a functionality to focus attention on more information of greater importance. It can also be explained as hiding away from the low-level functionality of a feature and exposing it via function or API so that it can be consumed. In programming, whenever you find yourself repeating the same code or logic, it's a good idea to create an abstraction. This is called the "Do not repeat yourself" rule (DRY). This means you take the detailed parts of the code and hide them, making it easier to use and understand. For example, instead of writing the same email-sending code in multiple places, you can create a function that handles it. This way, you only need to call the function whenever you need to send an email. This makes your code cleaner and easier to maintain. repeating logic in multiple places in your codebase, you should consider abstracting the implementation details into functions. ## Firebase Cloud Message Feature As a case study, a while back at Cudium, I needed to integrate notification into our application so that when a user makes a transaction, they are properly notified about the deposit or withdrawal made on their wallet via email and also via a notification so I decided to integrate with firebase after a few days of research. One thing stood out during my research, I needed to support two things : 1. Cross-platform notification - Android, Web and iOS. All of which Firebase Cloud Messaging (FCM) support 2. Support unicast, broadcast and also topic base subscription Since all the libraries I use at work are built on top of [Express JS](https://expressjs.com/), they help avoid bootstrapping Express applications from scratch, Sails JS, I mean, which is another form of abstraction. I decided to write a helper file to support this. Think of it as a utility library function, but in Sails JS, it goes beyond that. Let's take a look at it and develop an interesting approach that will help us create a 3-in-1 abstraction for centralized notifications later. > While the idea is language agnostic , I will be using Javascript to explain. Let's start with setting up the configuration for the Firebase cloud message which allows us to import all necessary constants ![temp-1](https://hackmd.io/_uploads/SkRuhS4H0.jpg) > *All configuration specific to firebase are extracted from the docs at*[*https://firebase.google.com/docs/cloud-messaging*](https://firebase.google.com/docs/cloud-messaging). Ensure you have firebase admin library installed Extracting the configuration is not the only thing needed to set up a Firebase cloud message notification on the backend, you also need to initialise the Firebase itself to be able to use it like so ![temp-2](https://hackmd.io/_uploads/rJmTnrNSR.jpg) Moving forward, since working with Sails JS requires using pure JavaScript and not TypeScript, I believe TypeScript is not necessary for every project, depending on your team's goals. Sails allows us to specify the type of data we expect to be passed into our functions or controllers, which makes using TypeScript redundant. The way arguments are specified to function in Sails Js is via property specification in this case for us to be able to set Firebase notification as a helper/util library we need to specify some expectation for the argument that the Firebase cloud message library expects to properly send notification across supported platforms such as device token, data, title, body, priority, time to live, topic, mutableContent, contentAvailable e.t.c. To do this, we need to specify the arguments in a table, which are expressed as a JavaScript object following Sail's JS conventions. You can see how they are defined [here](https://sailsjs.com/documentation/concepts/helpers#?inputs). | **Input** | **Type** | **Description** | **Defaults To** | **Example** | | --- | --- | --- | --- | --- | | `fcmDeviceTokens` | JSON | An array of unique FCM notification tokens for each device | | `['<token_1>']` | | `data` | JSON | Data to send to the client for the notification | {} | `{"account": "savings", "amount": 900.0}` | | `title` | string | Push notification title on delivery | `""` | `Debit Alert` | | `body` | string | Push notification message body on delivery | `""` | `msg: A deposit has been made to your account` | | `contentAvailable` | boolean | Used when sending messages to iOS | `true` | | | `mutableContent` | boolean | This applies only on iOS to allow the device to mutate the content before the presentation | `true` | | | `priority` | string | The delivery priority value for the target audience (allowed values: `high`, `normal`) | `PRIORITY` | | | `timeToLive` | number | Time in seconds to hold the message if the device is offline | `TIME_TO_LIVE` | | | `topic` | string | Topic to broadcast to | `""` | | > Side note: In order to be able to send notification to users, I needed to map every user to assigned device token as shown in the code snippet below but since I am only focused on explaining how to bundle sms, fcm and email message as a unit library, I won't be talking about the mapping user to ensure unique messaging even when users change their devices but this snippet below gives a rough idea of what it is. ![11-temp](https://hackmd.io/_uploads/HkcWU8VBR.jpg) Now back from our little detour, I will share the code snippet for the FCM notification and then explain what it does. Let's go. ![Snap (2)](https://hackmd.io/_uploads/HJh0aS4BA.jpg) With the parameters specified above, designing a helper function to abstract the implementation details of all things notification was easy. First, the implementation starts with a guard clause to ensure all required parameters are provided. If any of `topic`, `fcmDeviceTokens`, `data`, `title`, or `body` are missing, the function returns `false` immediately. Then the `payload` , an object was built including the data and notification content (title and body). Default message options (`defaultMessageOpts`) such as `ttl` (time-to-live) and `priority` are set using the provided values or default constants (`TIME_TO_LIVE` and `PRIORITY`). This is what is passed over to the Firebase notification SDK. Secondly, the main focus was on figuring out whether the notification was of one of the three modes listed below : * **Unicast**: Sending a notification to a single device. * **Multicast**: Sending notifications to multiple devices. * **Broadcast to Topic**: Sending notifications to all devices subscribed to a specific topic. To determine this, the following conditions were used : * **Unicast**: If there's only one device token and no broadcasting topic. * **Multicast**: If there are multiple device tokens and no broadcasting topic. * **Broadcast to Topic**: If a topic is provided along with `mutableContent` and `contentAvailable`. Since sending notifications can fail, I wanted to ensure we retry sending a notification even after a failure. To achieve this, a loop was used to implement a retry mechanism with a backoff strategy, attempting to send the notification up to `MAX_NOTIFICATION_BACKOFF` times if not successful. For unicast mode, a notification is sent to a single device token. If the response is successful, `notified` is set to true; otherwise, a retry attempt message is logged. For multicast mode, notifications are sent to all device tokens using `sendEachForMulticast`. The number of successfully sent notifications is logged, and if not all are successful, the failed tokens are identified. The list is compacted to remove null values, and retries are made for the failed tokens. For broadcasting to a topic, notifications are sent to devices registered to the specified topic. If the response contains `messageId`, it is marked as successful and `notified` is set to true; otherwise, a retry attempt message is logged. With this, we have the Firebase utility ready to support all three modes of notification. Now, let's set up the email side of things. ## Email Notification Feature Many applications need an email support feature, and you can integrate any email service based on your software requirements. For this article, we chose to integrate with Zoho Mail, even though the previous integration was with SendGrid. Therefore, I also had to ensure backward compatibility. ![Snap (3)](https://hackmd.io/_uploads/HJk51UEBC.jpg) By listing all the supported email providers in the code above, the application ensures reliable email delivery by using multiple transporters: Zoho Mail and SendGrid. The email payload, combined with a flexible template configuration, allows the application to easily integrate and send well-formatted emails. Let's quickly move to SMS notifications before discussing the centralized notification code. ## Sms Notification Feature To send notifications, we configure an Axios instance to make HTTP POST requests to the SMS provider. This ensures delivery to users whenever a pin or token is needed, as shown in the code snippet below. ![temp-4](https://hackmd.io/_uploads/SkiieL4SC.jpg) So with these three utility functions, let's create a centralized notification function that can send notifications via the three channels depending on varying configurations. ## Centralized Notification To centralize SMS, FCM, and Email notifications into a single utility function, we made some basic assumptions. We need to specify the type of notification to send and the necessary payload. This allows us to create a generalized function that builds on the three notification features discussed earlier. ![image](https://drive.google.com/file/d/1oj6NGkHGYyKjRk9HkjTFQ45cI_3Nj-oU/view?usp=sharing) The centralized notification library in the snippet above is designed to send emails, SMS, and push notifications to users. It starts by setting up constants for different notification types and allowed notifications. The required inputs include template options, data for the templates, the type of notification to send, and the recipient's user ID. When the function runs, it first gets the user's email and first name using the provided user ID. For email notifications, if the notification type is 'ALL' or 'EMAIL', it creates the email using the given template and data, sends it, and logs the result. Similarly, for SMS notifications, it checks if the type is 'ALL' or 'SMS', and sends an SMS with the provided data. For push notifications, if the type is 'ALL' or 'PUSH', it retrieves the appropriate template and data, maps the user's FCM device tokens, and constructs the push notification message. The message is then sent. The library ensures each notification type is only attempted if the required data and templates are provided, and logs errors if any data is missing. It uses necessary helper functions to handle the sending process, ensuring a robust and flexible notification system. This approach guarantees that notifications are reliably sent through multiple channels, enhancing user communication. While this may seem like a lengthy process for sending notifications, emails, or SMS to users, it ensures that each component can work independently and also together. One example of how this can be applied is during the KYC process for a new user, as shown in the snippet below. ![Snap (5)](https://hackmd.io/_uploads/rktfW8VHA.jpg) A similar use case could be for sending out transaction notifications to users after a debit or credit process. > While I know this article does not real give you the in depth step by step process of writing this whole logic out . My hope is that it gives you an idea of how you can set up all notification related logic in one library and use it at will. In summary, I hope this article lays in your heart the importance of abstraction using function and how to build better software with it Resources: * [https://sailsjs.com](https://sailsjs.com/) * [https://en.wikipedia.org/wiki/Abstraction\_(computer\_science)](https://en.wikipedia.org/wiki/Abstraction_(computer_science)) * [https://firebase.google.com/docs/cloud-messaging](https://firebase.google.com/docs/cloud-messaging) > ***I am Caleb and you can reach me at*** [***Linkedin***](https://www.linkedin.com/in/adewole-caleb) ***or follow me on***[***Twitter***](https://x.com/Soundboax)

Import from clipboard

Advanced permission required

Your current role can only read. Ask the system administrator to acquire write and comment permission.

This team is disabled

Sorry, this team is disabled. You can't edit this note.

This note is locked

Sorry, only owner can edit this note.

Reach the limit

Sorry, you've reached the max length this note can be.
Please reduce the content or divide it to more notes, thank you!

Import from Gist

Import from Snippet

or

Export to Snippet

Are you sure?

Do you really want to delete this note?
All users will lose their connection.

Create a note from template

Create a note from template

Oops...
This template is not available.
Upgrade
All
  • All
  • Team
No template found.

Create custom template

Upgrade

Delete template

Do you really want to delete this template?
Turn this template into a regular note and keep its content, versions, and comments.

This page need refresh

You have an incompatible client version.
Refresh to update.
New version available!
See releases notes here
Refresh to enjoy new features.
Your user state has changed.
Refresh to load new user state.

Sign in

Forgot password

or

By clicking below, you agree to our terms of service.

Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
Wallet ( )
Connect another wallet

New to HackMD? Sign up

Help

  • English
  • 中文
  • Français
  • Deutsch
  • 日本語
  • Español
  • Català
  • Ελληνικά
  • Português
  • italiano
  • Türkçe
  • Русский
  • Nederlands
  • hrvatski jezik
  • język polski
  • Українська
  • हिन्दी
  • svenska
  • Esperanto
  • dansk

Documents

Help & Tutorial

How to use Book mode

How to use Slide mode

API Docs

Edit in VSCode

Install browser extension

Get in Touch

Feedback

Discord

Send us email

Resources

Releases

Pricing

Blog

Policy

Terms

Privacy

Cheatsheet

Syntax Example Reference
# Header Header 基本排版
- Unordered List
  • Unordered List
1. Ordered List
  1. Ordered List
- [ ] Todo List
  • Todo List
> Blockquote
Blockquote
**Bold font** Bold font
*Italics font* Italics font
~~Strikethrough~~ Strikethrough
19^th^ 19th
H~2~O H2O
++Inserted text++ Inserted text
==Marked text== Marked text
[link text](https:// "title") Link
![image alt](https:// "title") Image
`Code` Code 在筆記中貼入程式碼
```javascript
var i = 0;
```
var i = 0;
:smile: :smile: Emoji list
{%youtube youtube_id %} Externals
$L^aT_eX$ LaTeX
:::info
This is a alert area.
:::

This is a alert area.

Versions and GitHub Sync
Upgrade to Prime Plan

  • Edit version name
  • Delete

revision author avatar     named on  

More Less

No updates to save
Compare
    Choose a version
    No search result
    Version not found
Sign in to link this note to GitHub
Learn more
This note is not linked with GitHub
 

Feedback

Submission failed, please try again

Thanks for your support.

On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

Please give us some advice and help us improve HackMD.

 

Thanks for your feedback

Remove version name

Do you want to remove this version name and description?

Transfer ownership

Transfer to
    Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

      Link with GitHub

      Please authorize HackMD on GitHub
      • Please sign in to GitHub and install the HackMD app on your GitHub repo.
      • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
      Learn more  Sign in to GitHub

      Push the note to GitHub Push to GitHub Pull a file from GitHub

        Authorize again
       

      Choose which file to push to

      Select repo
      Refresh Authorize more repos
      Select branch
      Select file
      Select branch
      Choose version(s) to push
      • Save a new version and push
      • Choose from existing versions
      Include title and tags
      Available push count

      Upgrade

      Pull from GitHub

       
      File from GitHub
      File from HackMD

      GitHub Link Settings

      File linked

      Linked by
      File path
      Last synced branch
      Available push count

      Upgrade

      Danger Zone

      Unlink
      You will no longer receive notification when GitHub file changes after unlink.

      Syncing

      Push failed

      Push successfully