Simple In-App Notifications Process

Most modern applications require some sort of in-app notifications, notifications that are sent to active users.

If you haven’t built something like this before, you may wonder how to start. Allow me to introduce you to a good solution to start with.

I have to emphasize that all these data fields and the process steps are highly adjustable. You can add, remove, or change anything according to your needs. This solution is a good start if you don’t have one. My advice is: don’t complicate the process if you don’t have specific needs or requirements. This process is simple and more than enough in most cases.

The Process. Including all functions that should be handled

Back End
Back End
Authenticate user
Authenticate user
Authenticated
Authenticated
Not Authenticated
Not Authenticated
create websocket
and save socket id in database/cache
create websocket…
return error
return error
return socket id
return socket id
create/update a database record marked the message as seen
create/update a data…
delete all data related to the messages from the database tables and cache
delete all data rela…
delete messages
scheduled job
delete messages…
calculate message expire and delete dates based on category configurations
calculate message expi…
event
occurred
event…
Categorize the message according to event type
Categorize the message…
Event for
specific user
Event for…
Public 
event
Public…
Retrieve user connection id from database/cache
Retrieve user connec…
Send the message to the user on the private connection
Send the message to…
Save the message in database/cache
Save the message in…
no current
connection
no current…
Send the message to the public connection for all active users
Send the message to…
Connection
is found
Connection…
retrieve all messages that msg_delete_data is today
retrieve all message…
retrieve all messages  for this user that is not seen, acked or expired
retrieve all message…
send messages over WebSocket connection
send messages over…
Front End
Front End
user open the app
user open the app
request websocket connection
request websocket co…
if socket id
if socket id
if error
if error
start listening to private and public channel
start listening…
display error
display error
display the message according to its category
display the message…
Web Socket
received a message
Web Socket…
send seen message over websocket connection
send seen message ov…
display the message according to its category
display the message…
Web Socket
received a message
Web Socket…
user saw the message
user saw the message
create/update a database record marked the message as acked
create/update a data…
send acked message over websocket connection
send acked message o…
user acked the message
user acked the message
websocket initiate closing event
websocket initiate c…
websocket closing
websocket closing
delete websocket data from database
delete websocket dat…
server closing
the websocket
server closing…
Text is not SVG – cannot display

This diagram is SVG. so you can zoom this page as much as you want. You will be able to read the text.

This process consists of 6 parts:

1- Any event occurring in our system that requires sending a notification to the user(s). This is the main part where message data is created in the database and prepared for sending or retrieval. In this part, we categorize the message and calculate its expiration and deletion date.

2- A user opens the application. This is where the system retrieves any unseen messages for the user.

3- A user sees the message. This is where the frontend informs the backend that the current user has seen the message, so the system will stop sending this message again. You can change this part so that the system continues displaying the seen message until it expires.

4- A user acknowledges the message. “Acked” is a shorthand for “Acknowledged”, which means the user not only saw the message but confirmed it by taking action. This action could be a button click. When a user acknowledges the message, the frontend informs the backend about it.

5- User connection is closed. If the user closes the app or the websocket connection is closed for any reason, the frontend will inform the backend so that the system can delete the connection data from the database.

6- Deletion time occurs. A scheduled job of any type has to run in the backend, searching for any messages where the deletion date is now or today. If any are found, all related data must be deleted from the database.

Database/Cache

A database is mandatory, while a cache is optional. A cache is a nice to have data layer, particularly if you have a large number of users and aim to speed the things up.

The cache becomes almost mandatory only when database transactions are significantly high, impacting the overall system performance.

If you have a cache layer, you must first search for data in the cache. If the data is not found in the cache, then you must search in the database. Additionally, remember to keep the cache updated to reflect the current data.

The ERD (Entity Relation Diagram)

A suggested database design for the notifications

Created with Fabric.js 5.2.4

Let me explain the reason of some data fields in message catagory table

msg_expire_after:
Messages are not sent or retrieved by users after a specific period of time. It could be seconds or anything else.
For example, a happy hour where there’s a discount or sale for only one hour. It doesn’t make sense to send a notification to the user after the happy hour is over.

msg_delete_after:
Messages are to be deleted from the database after a period of time. It could be days or anything else.
It’s not a good practice to keep most notifications.
For instance, if you’re sending news or updates to the user, this data is usually useless for the business, so we can delete it. In fact, most notification messages could be deleted.

require_ack:
If the message requires the user to click on a button to acknowledge the message. Some messages are important, so we need the user not only to see it but also to click on a button to confirm their understanding of the message content.


Connection information could be saved in the session table. I assume you implement token-based authentication in your system and keep the token of each session saved in the database. In this case, we can use the same session table and simply add the WebSocket connection data.

If we, and I recommend doing so, maintain only one connection per session, we can achieve that by using something like a shared worker in the frontend. By using a shared worker, if the user opens many tabs from your system, all tabs will use only one connection. Without a shared worker, every tab will require a separate connection. In this case, we will need to maintain multiple connections’ data per session, so we will need a new table.

Additionally:

  • You can create message templates that have placeholders for data.
  • Add a photo or icon to the message.
  • You can also add more options for each category. You may consider aggregating messages for a specific category that doesn’t require immediate user attention and send them as a batch every specific period of time.

WebSocket Security

WSS (WebSockets over SSL/TLS)
Clients have to provide an authentication token before starting a connection.
Every client can have up to a maximum number of connections (max_sessions_number).
You can use WebSockets directly or any wrapper library like Socket.IO, or even go further with pub/sub channels.

Post Written by Ahmed Metwally

More than 23 years of experience in the field of information systems.
Starting from IT and software development, then cloud and DevOps.
He has helped many companies adopt cloud technology. He held several diverse positions during his career.
From software developer to chief architect.

Similar Posts