In my previous post, I showed how you can enable an Azure Mobile Apps client to add tags to a push notification registration using the installation ID available on the client. While fairly popular, my original post was only for a .NET backend mobile app, and I got lots of requests to show an equivalent was to do the same in a Node.js backend.
NOTE: I’m also keeping an eye on an issue that the team is working on to whitelist tags so that clients can supply a set of pre-defined safe tags during registration. When that finally gets done, I will blog about it as it should be a bit easier than this, and not require an additional round-trip to the backend to add tags.
I finally had a chance to create a similar custom API in a Node.js backend Mobile App, with the goal of being able to have an identical API call to the .NET backend, where the POST request looks like this:
POST https://todolist.azurewebsites.net/api/updatetags/f211a45f-6f6d-4189-b106-e534af999fff HTTP/1.1 Host: todolist.azurewebsites.net Content-Type: application/json ZUMO-API-VERSION: 2.0.0 Content-Length: 20 ["test","broadcast"]
In the express.js app, the following POST method implementation defines a custom router that handles this request and adds tags to the given installation (remember that an installation is a push registration tagged with the client’s installation ID):
// Define a POST operation that updates installation tags. router.post('/:id', function (request, response) { // Get the notification hub used by the mobile app. var push = request.azureMobile.push; var installationId = request.params.id; // Get the tags array from the request message. var tags = request.body; // Validate for and block any SID tags. for (i = 0; i < tags.length; i++) { if (tags[i].search("sid:") !== -1) { response.status(403) .send("You cannot set '" + tags[i] + "' as a tag."); return; } } // Define an update tags operation. var updateOperation = [{ "op": "add", "path": "/tags", "value": tags.toString() }]; // Update the installation to add the new tags. push.patchInstallation(installationId, updateOperation, function(error, res){ if(error){ logger.error('An error occurred when adding tags', error); response.status(error.statusCode).send(error.detail); } else{ response.status(200).send(tags); } }); });
Note that this code validates the client-supplied tags to make sure a user ID isn’t being supplied, to prevent a user from getting push data for another user.
Just for completeness, here’s also the GET method that returns tags for a given registration:
// Define GET operation that returns tags for an installation. router.get('/:id', function (request, response) { // Get the notification hub used by the mobile app. var push = request.azureMobile.push; var installationId = request.params.id; push.getInstallation(installationId, function(error, installation, res){ if (error){ // Log the error and send an error response. logger.error('An error occurred when retrieving installation', error); response.status(error.statusCode).send(error.detail); } else{ // Return an array of current tags. response.json(installation.tags); } }); });
In particular, I wanted to keep the parameterized routes that I had in the .NET backend mobile app. Because of this, I wasn’t able to use the nice convenient JSON-based pattern for defining custom APIs in the /api subfolder. Instead, I ended-up having to use an express.js Router object to define my parameterized routes. Because I did this in a separate file in the ./api subfolder, I needed to expose my router as a module, which I consumed in the main app. Here’s how my router module is defined, without all the function code from above:
var express = require('express'); // Create an express.js router for our custom API. var router = express.Router(); // Define a POST operation that updates installation tags. router.post('/:id', function (request, response) { … }); // Define GET operation that returns tags for an installation. router.get('/:id', function (request, response) { …
}); module.exports = router;
Then, in the app itself, I added this app.use()
reference to hook-up to the ./api/udatetags
route:
app.use(mobileApp); // Add the router module on the custom API endpoint. app.use('/api/updatetags', require('./api/UpdateTags.js')); app.listen(process.env.PORT || 3000); // Listen for requests.
That’s all there is. I am planning on including this custom API and routing in the Node.js version of the quickstart completed project, which I hope to publish this week. Note that I’m not a Node.js guru, so if you have suggested improvements to my code, please let me know in the comments.
Cheers!