Using Webhooks with Realtime Code Hosting

  • Oct 2, 2016
  • Messaging
  • Webhooks
  • Serverless

Realtime Webhooks are HTTP callbacks triggered by a Realtime event, such as a new user connection or a message published. In simple terms a given endpoint will be invoked whenever some predefined event occurs in the Realtime network.

Realtime Code Hosting is a compute service where you can upload your Node.js code and run it using the Realtime infrastructure. Realtime takes care of provisioning and managing the servers that you use to run the code.

In this guide we'll be using the same simple validation example used in the Realtime Webhooks Starting Guide and in the end we'll look into using external API's through Mashape.

Configuring Realtime Code Hosting

Before you can create your Realtime Code Hosting function you need to subscribe to the Realtime Storage service and activate the Code Hosting feature.

Subscribing Realtime Cloud Storage

If you haven't yet subscribed to Realtime Cloud Storage log in to your Realtime Account and click Add subscription. If you already have a Storage subscription you can skip this step.

Expand the Realtime Cloud Storage panel, select the Free option and click the Subscribe button:

Enter a name for your new subscription and click the Create my Regular Storage Account button.

Activating Code Hosting

Go to your Realtime subscriptions list and expand the panel of your Realtime Cloud Storage subscription. Select the EDIT option as shown below:

Scroll down to the Code Hosting option and turn in on:

You have the option of creating a repository in the Realtime Git servers or use your own repository from GitHub, Bitbucket or any other Git platform, including your internal Git server.

After you have turned on the Code Hosting option click the SAVE button to deploy your private infrastructure. You'll see the following message:

After a few seconds your infrastructure will be ready to host your functions.

Click on the Mules Status Page link to monitor the progress. As soon as you see that your Code Hosting Mule is ready, the server instance responsible for hosting and executing your code, move on to the next section to create your first Code Hosting function.

Creating the Code Hosting function

Now that your Realtime Code Hosting infrastructure is ready you can proceed to creating your first function.

Click the Scripts button on the top menu as shown below:

By defaut your Realtime Code Hosting infrastructure is deployed with the following files:

  • package.json
    In this file you'll define your dependencies, the external modules from NPM that you want to use in your functions.
  • GETwelcome.js
    A simple welcome function exposed as a HTTP GET.

The next step is adding a new file with our validate function code:

  • Click the Add route button
  • Since the Realtime Webhooks are invoked through a HTTP POST select POST in the Type drop-down list
  • In the Name field enter your function name, validate
  • From the Arguments options select Pass 'request' argument and Pass 'modules' argument.

    These options will give you access to the POST request and the external modules you have defined in your package.json (along with some default modules used by Code Hosting).
  • Remove the test argument. Since Realtime will send the webhook payload in the request body we don't need extra parameters.
  • Paste the following code, replacing the code snippet generated by Code Hosting:

function validate(res,req,modules){
     
    // Your Realtime private key
    var privateKey = "ENTER-HERE-YOUR-REALTIME-PRIVATE-KEY";
     
    // Compute the event.body signature for security
    var signature = modules.crypto.createHmac("SHA256", privateKey).update(JSON.stringify(req.body)).digest('hex');
     
    // Get the triggers array
    var triggers = req.body.triggers;
      
    // iterate through all the triggers
    for(var t = 0; t < triggers.length; t++) {
        var isValid = false;
 
        // compare the signatures
        if(signature == req.headers['x-realtime-signature']) {
            // signature is valid
            // check if the trigger message has all the required fields
            try {
                var message = JSON.parse(triggers[t].message);
                if((message.sender && message.timestamp > 0 && message.text)) {
                    isValid = true;
                }
            }
            catch(e) {
                // message is not valid
            }
            finally {
                if(isValid) {
                    // the message is valid
                    triggers[t].statusCode = 200;
                } else {
                    // the message is invalid
                    // return an exception
                    triggers[t].statusCode = 400;
                    triggers[t].message = "Message is not valid"
                }
            }
        } else {
            // the message signature is not valid
            // return an exception
            triggers[t].statusCode = 403;
            triggers[t].message = "Message signature is not valid"
        }
    }
     
    // Send the response
 	res.send(200, triggers);
}

After you have pasted the code simply click the Commit & Deploy button to commit your code to the underlying Git repo and deploy it in your private infrastructure.

Enter a commit message - you may leave it blank and an automatic message will be generated - and click the OK button.

Your Code Hosting function will be ready to use after a few seconds when the message Commit and push completed is presented.

Your Code Hosting function was created and you can start using it with Realtime Webhooks.

Configuring the Realtime Webhook

Open your subscription list. Expand the Realtime subscription where you wish to configure the webhook and select the Webhooks option.

Select the Add new webhook button and enter the appropriate data into the form fields as explained below:

  • Event
    Select A message was published
  • Channel
    Enter the channel name to bind the webhook. Let's use chat:*
  • URL
    Select the validate function from the drop-down list (the function URL will be automatically entered in the URL field).

    If you don't see the function in the list verify that you are using the right Realtime application key.
  • Execute
    Keep the Before message is delivered to subscribers option selected

And that's it!

Click the SAVE button to start using your Realtime Code Hosting function to validate the messages sent to all sub-channels of the chat channel.

Using external API's

There are thousands of API's out there ready to be integrated into your Realtime apps.

In this section we'll see how we can use an external API from a webhook function running in Realtime Code Hosting.

Translating chat messages

Let's stick to our chat example from the Webhooks Starting Guide with the following message format:

{
	"sender": "John Smith",
	"timestamp": 1474298409178,
	"text": "Hi there! What a great day for a chat!"
}
        

Our goal is to automatically translate the message sent to several languages and add these translations to the chat message as new properties. The channel subscribers will then be able to select which language they want to see.

Through the Mashape Market we'll use the SYSTRAN.io Translation API, a collection of APIs for Translation, Multilingual Dictionary lookups, Natural Language Processing and much more.

The translate API receives a string to translate and a target language, returning the translated string. As simple as that!

Our webhook function will be executed before the message is sent to the subscribers, so the function is able to call the SYSTRAN translate API with the trigger message and return the original chat message augmented with the translations.

In this function we'll also use several external NPM modules to make our life easier, so don't forget to add them to your Code Hosting package.json file:

{
  "dependencies": {
	"async": "^2.0.1",
	"qs": "^6.2.1",
	"node-fetch": "^1.6.0",
	"bluebird": "^3.4.6"
  }
}

The Code Hosting function code to translate the messages to English, French, Portuguese and Japanese can be found below (the original language is detected automatically by the SYSTRAN API):

function translate(res,req,modules){
    
    // translation api key
    var apiKey = 'ENTER-YOUR-MASHAPE-APIKEY';

    // translation api url
    var apiUrl = 'https://systran-systran-platform-for-language-processing-v1.p.mashape.com/translation/text/translate';

    // external module to invoke the external API
    var fetch = modules["node-fetch"];

    // external module to implement Promises
    fetch.Promise = modules.bluebird;
    
    // external module to handle the API parameters
    var qs = modules.qs;
    
    // target languages
    var langs = ['en', 'fr', 'pt', 'ja'];

    // the webhook triggers
    var triggers = req.body.triggers;
    
    // iterate through all triggers asynchronously
    modules.async.each(triggers, function(trigger, callback) {

    	// the chat message
        var message = JSON.parse(trigger.message);
        
        // iterate through all target languages
        modules.async.each(langs, function(lang, callback) {

        	// the API parameters
            var queryParams = {
                source: "auto",
                target: lang,
                input: message.text,
                'mashape-key': apiKey
            };
            
            // the final API URL to GET
            var url = apiUrl + '?' + qs.stringify(queryParams);
            
            // Invoke the API
            fetch(url)
                .then(function(res) {
                    return res.json();
                }).then(function(json) {
                    if(json.outputs && json.outputs['0'] && json.outputs['0'].output) {
                        // get the translated message and add it to the original message
                        var translatedText = json.outputs['0'].output;
                        message["lang_" + lang] = translatedText;
                    }
                    
                    // iterate to next language
                    callback();
                });
        }, function(err) {
            // all languages were processed and stored in the message
            // update the original message in the trigger object
            trigger.message = message;

            // iterate to next trigger
            callback();   
        });
    }, function(err){
        // all triggers messages translated
        // return them to the Realtime Messaging server
        // for delivery to subscribers
        res.send(200, triggers);
    });
}

The function has a few lines of code but it's easy to understand. We simply iterate through the triggers array to handle all chat messages. For each chat message we iterate through the target languages array to get the message translation.

When all translations are performed we return the new messages, containing the original chat message in the text property and the translations in new properties prefixed with lang_ followed by the language ISO code.

A "Hello World" chat message would look like this after the translation webhook processing:

{
	"sender":"John Smith",
	"timestamp":1474298409178,
	"text":"Hello World",
	"lang_ja":"こんにちは世界",
	"lang_es":"Hola mundo",
	"lang_pt":"Olá! mundo",
	"lang_fr":"Bonjour monde"
}

Click here to see a simple multilingual chat demo, using this translate function configured as a webhook and hosted in Realtime Code Hosting.

Pretty neat, right?

Get your free Realtime account now

Signup here

If you find this interesting please share: