There’s been some recent discussion on the hal-discuss mailing list around the ‘Zoom’ concept for embedding resources, which has prompted me to start exploring this area a little further with a view to potentially writing up a specification that can be applied to any media type that define a way of embedding resources. This blog post mostly serves as a narrative to document my own ideas and learnings and to collect comments and recommendations from others in order to arrive at a proposal that will work for (almost!) everyone.

At its most simple level, clients need a way of being able to take an existing resource with defined link relations and zoom (or expand) the current resource to include them in the response. This is primarily to avoid a second round trip to the server in order to retrieve the resource representation that the client is interested in seeing.

To illustrate the problem, I have provided an example (for the sake of brevity, I have only included relevant parts of the resource representation).

{
    "_links": {
        "http://.../rels/detail": {
            "href": "/example/1/detail"
        },
        "http://.../rels/misc": {
            "href": "/example/1/misc"
        },
        "self": {
            "href": "/example/1"
        }
    }
}

In the above example, imagine a scenario where the client that has requested this representation is actually interested in the resource found at the ‘http://…/rels/detail’ link relation. The client now has to make a second request to the server in order to get at the data it needs.

We can alleviate this by using a feature of the media type known as embedding – however it is impractical to embed every linked resource every time. We must provide a way of allowing the client to specify the resource it is interested in – and this is ‘Zoom’.

If we allow the ‘zoom’ (or ‘z’) parameter in the query string to be reserved for this purpose, we can request that the server embeds the link relation by performing a GET request on ‘http://…/example/1?zoom=http://…/rels/detail’. The server would then be given the instruction to embed the resource and return it all in the response.

{
    "_embedded": {
        "http://.../rels/detail": [
            {
                "_links": {
                    "self": {
                        "href": "/example/1/detail"
                    }
                },
                "firstname": "Ben",
                "lastname": "Longden"
            }
        ]
    },
    "_links": {
        "http://.../rels/detail": {
            "href": "/example/1/detail"
        },
        "http://.../rels/misc": {
            "href": "/example/1/misc"
        },
        "self": {
            "href": "/example/1"
        }
    }
}

If we wanted to embed two items, the zoom query parameter may accept multiple link relations separated by commas, i.e., http://…/example/1?zoom=http://…/rels/detail,http://…/rels/misc.

The HAL format also supports CURIE style links to shorten the length of link relations and abbreviate the link down to a ‘x:detail’ or ‘x:misc’ format. There is no reason to not support these shorter syntax link rels, if the CURIE can be resolved. Here is an example of ‘http://…/example/1/?zoom=x:detail’.

{
    "_embedded": {
        "x:detail": [
            {
                "_links": {
                    "self": {
                        "href": "/example/1/detail"
                    }
                },
                "firstname": "Ben",
                "lastname": "Longden"
            }
        ]
    },
    "_links": {
        "curies": {
            "href": "http://.../rels/{rel}",
            "name": "x",
            "templated": true
        },
        "self": {
            "href": "/example/1"
        },
        "x:detail": {
            "href": "/example/1/detail"
        },
        "x:misc": {
            "href": "/example/1/misc"
        }
    }
}

The above examples and narrative cover zoom at it’s most basic implementation – and this will fit many situations fairly well. However, this idea of partially embedding sub resources is not new – what we are seeking is a unified way of following this pattern so that a common approach can be applied to any media type that has the ability to embed resources.

Implementations of this pattern can be found ‘in the wild’ in a number of places – to varying levels of completeness. From simply being able to embed a resource in a representation through to being able to specify a DSL to allow clients to query data that’s returned (specifying a limit to the number of resources in an embedded collection, or filtering by values in an embedded collection.

I’ll write up some thoughts around querying and limiting embedded data in a future post – i’d like to keep things relatively simple for now, and dive deeper into what the possibilities are later!

Comments more than welcome below.

Share →

14 Responses to Expanding Zoom

  1. I wonder how to decide how many levels of zoom should be supported. “1” seems a bit arbitrary, but an infinite level would mean that you can pretty much download an entire website with one request. (If every resource is connected to every other resource, hypermedia style.)

    Zooming could play havoc with caching too, so I guess how useful this is depends on how often the embedded (i.e. unaggregated) resources would otherwise be available in a cache of some sort. (For example see Stack Exchange’s approach to embedding: https://api.stackexchange.com/docs/vectors. That’s gonna be painful if every client wants a different set of embedded resources!)

  2. @michael I guess zoom could be a suggested expansion level for those situations, where the server isn’t obligated to expand up to that level. It does require the client library to be aware they might not get the level they want, but if resources are transparently requested based on the response metadata on data access, that shouldn’t be an issue.

  3. hdave says:

    I like this idea a lot. Sure its not recursive, sure it doesn’t provide infinite flexibility in levels, but there are a lot of times where you only can go only one level down anyway because you need to discover the available link relations down there in order to fetch the second level down.

    In any event, having this control could really make a bit dent in chattiness and isn’t that what this is all about anyway?

    If I really wanted a solution that went all the way down in recursion, I’d probably define a new kind of resource called a zoom overlay or zoom profile and allow client to define those and later use/combine/merge them from the zoom query parameter. Not very different from creating a search resource and later executing it to create a view of resources.

  4. Luke Stokes says:

    Great post, Ben. Great to see you again this year at RESTfest as well. If anyone wants to play with the zoom concept in a sandbox, you can check it out on our API here:

    https://api-sandbox.foxycart.com/hal-browser/hal_browser.html#https://api-sandbox.foxycart.com/stores/8/transactions?zoom=items,payments

    The payments and items in that link are zoomed in as compared to https://api-sandbox.foxycart.com/hal-browser/hal_browser.html#https://api-sandbox.foxycart.com/stores/8/transactions

    You can also link a couple layers deep (such as item options)

    More on the docs here: https://api.foxycart.com/docs

    Let me know if you have any questions or if I can help with anything you’re working on to extend or standardize the zoom concept.

  5. Matt Bishop says:

    It was great to hear you at REST Fest this year Ben!

    Our zoom feature does have a depth limit of 5, but most zooms max out at 3 levels. On the server the requests are concurrent which is something that a browser cannot do effectively given the 2 requests-per-size limit on all browsers. We also have a server-level cache to keep the perf acceptable.

    My main concerns are the size of the payload. The client dev can download a lot of data without realizing it. We have managed this with paginated resources, like search and navigations, so your zoom only works on page 1, for instance.

  6. Matt Bishop says:

    Also the cacheability is pretty good because the zoom is in URL; if you have a public resource that zoom request will be cached just like any other request. The one caveat of course is that zooms will need to be constructed in the same order, like ‘price,assets’ vs. ‘assets,price’.

  7. @rzazueta says:

    Zoom is an elegant solution to the hypermedia problem of reducing the calls to get detailed data. http://t.co/nQgBUgwhcl #restfest

  8. graste says:

    Thanks for the blog post and comments (foxycart :-)). Good solution, but I’m wondering how one would use something like that for HTML pages where e.g. a list of embedded items is displayed and there are multiple variants of a list item (like a full document w/ all fields; only some important fields; some other important set of fields; only necessary fields). A simple zoom level would be not sufficient in that case. Another resource (list view) is not necessarily needed as well (may just be a variation depending on a filter query).

  9. @mwop says:

    @elazar Different resource, zoom (http://t.co/8xp0OG1PzP), or multiple representations via links. /cc @jperras

  10. @SeanTAllen says:

    “Expanding Zoom” => http://t.co/k0WAnUpjPC #Hypermedia and embedding resources #HAL #REST

  11. @rgarver says:

    Expanding Zoom | Ben Longden http://t.co/KdGTXoyPdF

  12. @adamwathan @stauffermatt I’ve used “zoom” in the past, some reading from @blongden here http://t.co/gtxPP1Potj

  13. @beyond_code Sounds like you are trying to do what @blongden calls zoom https://t.co/iz6ETkEVg3

Leave a Reply

Your email address will not be published. Required fields are marked *