Adding push notification tags from the client–Node.js backend version


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!

Glenn Gailey

Skip to main content