Subscribe Now

* You will receive the latest news and updates on your favorite celebrities!

Trending News

Blog Post

Building Custom Cloud messaging API- The Server
Android, Firebase, Node.js, Tutorials

Building Custom Cloud messaging API- The Server 

This is the 2nd part of the article on how to build a custom cloud messaging API for android. In this part, we will try to build a server application on Node.js and send push notifications to our android app.

You can find the first part of this article here, Building a custom Cloud Messaging API for Android.

Note: This application works with the corresponding client SDK only. It may not work with other custom clients as intended. This article also demonstrates how to override and handle custom events emitted by server so that you can create your own custom client and server application.

Adding Dependencies

  1. Socket.io for Websockets: WebSocket is a protocol for bi-directional communication. We are using Socket.io library for our simple server application. If you want you can write your custom socket library for this, but here I am using a pre-tested and reliable library for this purpose.
  2. Express for API Endpoint: Express is a really famous framework used for HTTP requests handling and many other server side logics. Here we will use express to expose an endpoint to send custom notifications to user.
 npm install socket.io express

Connecting to Client

The very first operation for a CM server is to initialize the socket and start listening for connections. Let’s see how we can start listening for connection requests in Socket.io server.

clientconnection.js

const socketio = require("socket.io");

let http = null;
let io = null;

const initConnection = (app, PORT) => {
  return new Promise((resolve, reject) => {
    try {
      //Create socket
      http = require("http").Server(app);
      io = socketio(http,{
        cors: {
          origin: "http://server-ip.com",         //change this to your server-ip or domain 
          methods: ["GET", "POST"]
        }
      });

      //listen for events in socket
      http.listen(PORT, () => console.log("Socket listening on port: " + PORT));

      //resolve io instance
      resolve(io);
    } 
    catch (e) 
    {
      reject(e);
    }
  });
};

module.exports = { initConnection };

initConnection is a promise which returns a socket server instance which will be used later. Here is a small snippet on how to use this module.

 deviceConnection.initConnection(app, PORT).then((io) => {
        doSomethingWith(io);
    }).catch(err=>reject(err));

Handling events in Socket.io

A ‘connection’ event is fired when a new client is connected to the server. We will receive those events and log a the socket ID to address the client.

//To listen to messages
  io.on("connection", (socket) => {
    console.log("connected to socket " + socket.id);
    //Log client Id and socket id to a list or some kind of array
    console.log("user connected");
  });

Every connection has an ID called socketID and we can address the client only through their socketID. So, we need to implement some logic to map those socketID with the clients UserID ( or device ID in this case). If you have a large user base it is not recommended to store these informations in memory, but here for simplicity, I will be storing these data in memory itself.

Client-handshake

A client-handshake is a mandatory process for every client when it connects to the server (or when it re-connects). In this process we exchange useful information about the client to the device and if the client is authorized, the server allocates a socket for the client and stores its corresponding data.

//To listen to messages
  io.on("connection", (socket) => {

    let clientID = null;
    console.log("connected to socket " + socket.id);
  
    socket.on("client-handshake", (client, ack) => {
      console.log("handshake-recieved");
      client = JSON.parse(client);
      clientID = client.clientID;
      users[client.clientID] = socket.id;
      console.log("active users are:");
      console.log(users);
      ack("OK");
    });
    console.log("user connected");
  });

Also, we can handle some disconnect events to remove that user from the list of active users.

socket.on("disconnect", () => {
      console.log("Disconnected");
      if (clientID != null) users[clientID] = null;
      console.log(users);
    });

Here is the full code of cloud-engine.js

cloud-engine.js

const deviceConnection = require("./connections/client-connection");

const users = {};

const initEngine = (app, PORT) => {
  return new Promise((resolve, reject) => {
    deviceConnection.initConnection(app, PORT).then((io) => {
        registerEvents(io);
        resolve(io);
    }).catch(err=>reject(err));
  });
};



function registerEvents(io) {

  //To listen to messages
  io.on("connection", (socket) => {

    let clientID = null;
    console.log("connected to socket " + socket.id);
    
    socket.on("disconnect", () => {
      console.log("Disconnected");
      if (clientID != null) users[clientID] = null;
      console.log(users);
    });
    
    
    socket.on("client-handshake", (client, ack) => {
      console.log("handshake-recieved");
      client = JSON.parse(client);
      clientID = client.clientID;
      users[client.clientID] = socket.id;
      console.log("active users are:");
      console.log(users);
      ack("OK");
    });
    
    console.log("user connected");
  });
}

const notifyUser = (io,uid, title, content) => {
  return new Promise((resolve, reject) => {
    try {
      io.to(users[uid]).emit("cloud-event-push", title, content);
      resolve(true);
    } catch (e) {
      reject(e);
    }
  });
};

const send=(io,uid,eventName,...args)=>{
  return new Promise((resolve, reject) => {
    try {
      io.to(users[uid]).emit(eventName,...args);
      resolve(true);
    } catch (e) {
      reject(e);
    }
  });
}

const notifyAll = (io, title, content) => {
  return new Promise((resolve, reject) => {
    try {
      io.emit("cloud-event-push", title, content);
      resolve(true);
    } catch (e) {
      reject(e);
    }
  });
};

module.exports = { initEngine, send, notifyUser, notifyAll };

How to use?

After creating modules, we can now proceed on creating the web app itself. We will be using express to handle all HTTP requests. Here is the code for app entrypoint.

index.js

const express=require('express');
const cloudEngine=require('./cloud-engine');

const app=express();
var cloudapp;

//This is socket port. Server will be listening for connections on this port.
const PORT=3001;

initAllSystem();

//Initializing Cloud Engine
async function initAllSystem()
{
    try{
        cloudapp = await cloudEngine.initEngine(app, PORT);
        if(!cloudapp)
            throw "cloud app not initialized";

        app.post('/',(req,res)=>{
            cloudEngine.send(
              cloudapp,
              "device-id",
              'cloud-event-push',
              "Welcome",
              "How are you doing?"
            );
        })
        app.listen(3000,()=>console.log("Started listening on "+3000));
        
    }catch(err)
    {
        console.log(err);
    }
    
}

The JS-Cloud Library

If you dont want to write your own code for handling connections, I have published a lightweight NPM library for the same. Here is a small demo on how to use this library,

Add the library

npm i js-cloud-messaging-api

initializing cloud server:

cloudapp = await cloudEngine.initEngine(app, PORT);

send push notification to android client

cloudEngine.notifyUser(
              cloudapp,                       //cloud-engine instance
              "device-id",                    //UID or Device ID 
              "Welcome",                      //Notification Title
              "How are you doing?"            //Notification content
            );

send push notification to all connected clients

cloudEngine.notifyAll(
              cloudapp,                      //cloud-engine instance
              "Welcome",                     //Notification Title
              "How are you doing?"           //Notification Content
            );

send custom message to client

cloudEngine.send(
              cloudapp,                     //cloud-engine instance
              "device-id",                  //device-id
              'custom-message',             //custom-message-tag
              "Custom Message",             //Message Title
              "This is custom message"      //Message Content
            );

Here is a demo code on how to implement the entire system.

const express=require('express');
const cloudEngine=require('js-cloud-messaging-api');

const app=express();
var cloudapp;

const S_PORT=3001;
const API_PORT=3000;

initAllSystem();

//Initializig Cloud Engine
async function initAllSystem()
{
    try{
        cloudapp = await cloudEngine.initEngine(app, S_PORT);
        if(!cloudapp)
            throw "cloud app not initialized";
        
        //route for sending custom-message 
        app.post('/send',(req,res)=>{
           cloudEngine.send(
              cloudapp,                     //cloud-engine instance
              "device-id",                  //device-id
              'custom-message',             //custom-message-tag
              "Custom Message",             //Message Title
              "This is custom message"      //Message Content
            );
        })

        //route for sending notification to single user
        app.post('/notifyUser',(req,res)=>{
            cloudEngine.notifyUser(
              cloudapp,                       //cloud-engine instance
              "device-id",                    //UID or Device ID 
              "Welcome",                      //Notification Title
              "How are you doing?"            //Notification content
            );
        })

        //route for sending notification to All user
        app.post('/notifyAll',(req,res)=>{
            cloudEngine.notifyAll(
              cloudapp,                      //cloud-engine instance
              "Welcome All",                 //Notification Title
              "How are you doing?"           //Notification Content
            );
        })
        app.listen(API_PORT,()=>console.log("Started listening on "+API_PORT));
        
    }catch(err)
    {
        console.log(err);
    }
    
}

Conclusion

This library only supports direct messaging as of now, but I plan to include some other features like topic messaging as in firebase which allows user to subscribe to a topic and receive notifications for that topic.

Thank You!

Related posts

Leave a Reply

Required fields are marked *