Nested Resources

Chapter 8: How to create nested resources with resource references

In most cases we need to be able to create more complex data structures than flat objects and arrays.

Let’s say we want a list of items, such as this:

[
	{ "id": 1, "item": "foo" },
	{ "id": 2, "item": "bar" },
	{ "id": 3, "item": "baz" }
]

Let’s take a look at how to build it.

Resource references

With Resgate and the RES protocol, the services only serves resources separately, linking them together with resource references1. A resource reference is a type of value1 represented by a JSON object with the property:

  • rid - resource ID string of the referred resource

It could look like this:

{ "rid": "example.item.1" }

Tip

A microservice may have resources with references to resources served by other microservices.

Serving the list

Each item in the example at the beginning of this chapter is a model, and the list itself is a collection. Each of these resources must have their own unique resource ID.

The models can have the resource IDs example.item.1, example.item.2, and example.item.3.
The collection can have example.items (though nothing prevents us from using example.item if that is preferred).

Serving the resources could look like this:

// Define our models
let items = {
	"example.item.1": { "id": 1, "item": "foo" },
	"example.item.2": { "id": 2, "item": "bar" },
	"example.item.3": { "id": 3, "item": "baz" }
};
// Define our collection of resource references
let list = [
	{ "rid": "example.item.1" },
	{ "rid": "example.item.2" },
	{ "rid": "example.item.3" }
];

// Serve items
nats.subscribe('get.example.item.*', (req, reply, subject) => {
	// Get resource ID by removing "get." from the subject
	let rid = subject.substr(4);
	// Look up item. This is often done towards a database
	let item = items[rid];
	if (item) {
		nats.publish(reply, JSON.stringify({
			result: { model: item }
		}));
	} else {
		nats.publish(reply, JSON.stringify({
			error: { code: "system.notFound", message: "Not found" }
		}));
	}
});

// Serve list
nats.subscribe('get.example.items', (req, reply) => {
	nats.publish(reply, JSON.stringify({
		result: { collection: list }
	}));
});

The benefits

By serving resources separately, the microservice only needs to focus on one resource at the time without having to lock all items while processing the request. It also enables Resgate to properly keep track on the changes of each item.

From a client perspective, the request for the item list will only seem like a single request, as Resgate will stitch together all the linked resources before responding to the client.

Warning

Try to avoid deep nesting. Each depth level will force Resgate to make another round of requests to the microservices, which will add latency.

Also, try to avoid large sets of data (like huge lists). Instead, try to send only what the client currently needs through pagination or similar solutions. This can be achieved using queries, as described in the Advanced Topic - Query Resources.