Distigme

A little learning is a dangerous thing.

SMD (Service Mapping Description)

Leave a comment

A decade ago, XML web services would describe themselves using the WSDL standard, which tools could use to generate a strongly typed and convenient wrapper for calling the service, such as a Java class. Fast forward halfway to today, and the challenge has moved on from accessing XML services from the server side to accessing JSON services from the browser.

Not that it’s ever been too difficult. JSON first became popular because it was already valid JavaScript (though that fact was only relevant to those willing to call the evil eval()). And JavaScript is dynamically typed. So calling a simple ajax method in modern Dojo might look like this:

require(["dojo/request"], function(request) {
	request.get("/ajax/comments/", {
		handleAs: "json",
		query: { id: 123 }
	}).then(function(result) {
		console.log(result.length + " comments.");
	});
});

JQuery code tends to have these direct ajax calls scattered everywhere. They look similar:

$.ajax({
	url: "/ajax/comments/",
	dataType: "json",
	data: { id: 123 }
}).then(function(result) {
	console.log(result.length + " comments.");
});

But the burden can be still lighter. What exactly was the URL? Does it go like comments/123 or comments?id=123? Was it a POST or a PUT? What if I forget a parameter that the server requires?

So, the natural evolution is to start writing wrapper functions in a new module:

define(["dojo/request"], function(request) {
	return {
		getComments: function(id) {
			return request.get("/ajax/comments/", {
				handleAs: "json",
				query: { id: id }
			});
		}
	};
});

Now the caller can be simplified:

require(["my/service"], function(service) {
	service.getComments(123).then(function(result) {
		console.log(result.length + " comments.");
	});
});

That’s progress, but it’s somewhat expensive progress, and you may question whether it’s really worthwhile for a method you only call once.

Enter SMD. Just write a JSON object (the SMD) that describes the API of your service, and Dojo can generate the methods.

define(["dojox/rpc/Service"], function(RpcService) {
	var smd = {
		"SMDVersion": "2.0",
		"id": "http://www.example.org/ajax/",
		"description": "Example Service",
		"target": "/ajax/", 
		"envelope": "URL",
		"services": {
			"getComments": {
				"target": "comments",
				"transport": "GET",
				"parameters": [
					{ "name": "id", "type": "integer" }
				]
			},
	return new RpcService(smd);
});

This example shows the SMD embedded in a Dojo module, but you can also put it in a separate file.

You can imagine that if a team has one Java or PHP expert writing the API and one JavaScript expert consuming the API, a single file to define the interface is a big help. If the back-end guy needs to change a URL, instead of sifting through the mountain of unfamiliar JavaScript and changing every ajax call, he just changes it in one place in one file. The front-end guy, meanwhile, can look at the SMD and see at a glance what services are available.

Better yet, it should be possible to generate the SMD from, say, a Spring application. I’m not aware of that having been done yet, though. But Struts seems to have something.

In fact, SMD seems to have not yet caught in the five years since Dojo began to support it. There is an old post on SitePen introducing it, and then… not a peep. The Dojo Toolkit now has some basic documentation on dojox/rpc (note that dojo/rpc is legacy), along with the SMD 2.0 proposal, but that’s about it. In contrast, JSON Schema, on which SMD is built, seems quite active. The lack of resources on modern SMD is what motivated me to write this post.

SMDs for a few popular sites (Google, Wikipedia, Twitter) are included in dojox/rpc/SMDLibrary. Naturally, since you would use these in a cross-domain scenario, they all go over JSONP.

Here is a longer example:

define(["dojox/rpc/Service", "dojox/rpc/Rest"], function(RpcService, RpcRest) {
	var smd = {
		"SMDVersion": "2.0",
		"id": "http://www.example.org/ajax/",
		"description": "Example Service",
		"target": "/ajax/", 
		"envelope": "URL",
		"services": {
			"setPassword": {
				"target": "my-password/",
				"transport": "POST",
				"parameters": [
					{ "name": "passwordOld", "type": "string" },
					{ "name": "password", "type": "string" }
				],
				"returns": { 
					"type": "object",
					"properties": {
						"isValidPasswordOld": { "type": "boolean" }
					}
				}
			},
			"status": {
				"target": "status",
				"envelope": "PATH",
				"transport": "GET",
				"parameters": [
					{ "name": "id", "type": "integer" }
				],
				"returns": "string"
			},
			"comment": {
				"target": "comment/",
				"transport": "REST",
				"parameters": [
					{ "name": "id", "type": "integer" }
				]
			}
	};
	return new RpcService(smd);
});

Here we see the setPassword method as a POST, returning a complex object. The status method builds a URL from its parameter like /ajax/status/123 and returns some text. comment is actually a REST endpoint supporting multiple methods (we have to explicitly require dojox/rpc/Rest for this to work). So now we can make calls like these:

require(["my/service"], function(service) {
	service.setPassword("old", "new");
	service.setPassword({ passwordOld: "old", password: "new" });
	service.comment.delete(86);
	service.status(42).then(function(status) {
		alert(status);
	});
});

You can call service methods two different ways, as illustrated here by setPassword: you can pass the parameters in order, the same order specified in the SMD, or you can pass an object that maps parameter names (if you named your parameters in the SMD) to their values, which is handy for when you can load such an object from a form, for example.

Every service method returns a promise, so you can and should handle the states of pending, succeeded, and failed, and you need then() or when() to access return values once they arrive.

SMD is pretty straightforward to use and has clear benefits for structuring your code on a large project. Better still, you can adopt it gradually, using SMD-generated services in some places and direct ajax calls in others. There is also potential for tools to make use of SMD in other ways (e.g., a handy test bench for manually invoking methods), but I don’t expect much of these until SMD sees wider adoption.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s