Methods

Chapter 6: How to define and call methods on a resource

Each resource may have methods that can be called remotely through a call request1. It is in many ways similar to a HTTP POST request. Let’s have a look at it.

Call requests

To listen for call requests, the service subscribes to a subject with the following pattern:

call.<resource>.<method>

Where resource is the resource ID and method is the alphanumeric (no dots!) name you’ve given to the method.

In code, listening to calls to method mymethod on resource example.foo would look like this:

nats.subscribe('call.example.foo.mymethod', (msg, reply) => {
	/* Handle the request and send a response */
});

Request message

The call request’s message is a JSON object with the following properties:

The message of a call request may look like this:

{
	"params": { "foo": "bar", "baz": 42 },
	"cid": "bhrq7ht8smgioj39ibr0",
	"token": null
}

Successful response

If the call is successful, the service sends a response2 with either a static result, or a resource reference.

A resource references will be resolved by Resgate, so by the time the client gets the response from the call, it will not just be a reference, but the actual resource. Whereas a static result will be passed untouched to the client.

Result

A result response is a JSON object with the following property:

  • result - result data as determined by the method. May be null.

In code it could look like this:

nats.subscribe('call.maths.utils.add', (msg, reply) => {
	let { params } = JSON.parse(msg);
	nats.publish(reply, JSON.stringify({
		result: params.a + params.b
	}));
});

Resource

A resource response is a JSON object with the following property:

  • resource - resource reference3 which is an object with a single rid string property.

In code it could look like this:

nats.subscribe('call.inventory.createItem', (msg, reply) => {
	let { params } = JSON.parse(msg);
	let item = createNewInventoryItem(params); 
	nats.publish(reply, JSON.stringify({
		resource: { rid: 'inventory.item.' + item.id }
	}));
});

Note

An HTTP POST request getting a resource response will result in an empty HTTP response body with a Location HTTP header containing the url to the referenced resource:

HTTP/1.1 200 OK
Location: /api/inventory/item/42

Error response

In case of an error, such as Invalid parameters or Method not found, the response2 may look like this:

{
	"error": {
		"code": "system.invalidParams",
		"message": "Invalid parameters"
	}
}

Look at the pre-defined errors4 in the specification for a full list of error codes.

Tip

If you use a custom error, the first part of the error code should be the unique name of the service.
This will help in avoiding naming conflicts between multiple microservices.

Pre-defined methods

The RES protocol describes pre-defined call methods5 that may be used for specific purposes. Let’s go through them.

Set call request

A set request6 may be used to update or delete a model’s properties. While the service is free to update a model at any time, triggered by any server-side event or client-side request, the set method is reserved for this purpose. This is to define a common way to update models. The subject has the method set:

call.<resource>.set

The parameters should be a JSON object where each property being the new value to set on the model. If a model property is to be deleted, a delete action7 should be sent by the client instead of a new or null value.

In case some model properties were changed, a model change event8 must be sent as described in Chapter 4 - Model Events. You must make sure the event is sent before the call response.

The response itself is the same as for custom call requests, described earlier in this chapter.

Tip

In case a different parameter structure is required, eg. for a compare-and-swap kind of update, it is recommended to create a custom method to handle that request.

By keeping the set parameters standardized, it is easier to create clients with generic CRUD operations.