resthateoas

Building the links key in RESTful response (for HATEOAS)


I created a geo API for Portugal and now I am trying to conform RESTful standards by introducing HATEOAS.

Example: this response is a list of parishes (freguesias) for a specific municipality (municipio) /municipios/{municipality}/freguesias

{
  "nome": "Porto",
  "freguesias": [
    "Bonfim",
    "Campanhã",
    "Paranhos",
    "Ramalde",
    "União das freguesias de Aldoar, Foz do Douro e Nevogilde",
    "União das freguesias de Cedofeita, Santo Ildefonso, Sé, Miragaia, São Nicolau e Vitória",
    "União das freguesias de Lordelo do Ouro e Massarelos"
  ]
}

Each parish (element of Array freguesias) has its own link: /freguesias/{parish}

How would you build the links key in the response?


Solution

  • In a narrow sense REST is not based on standards other than HTTP as its main transport layer, URI as its naming scheme and various well-defined media-type definitions which it uses to exchange messages with peers that are able to process such. REST is an architectural style, a set of indirections if you will that decouple clients from servers allowing a server to effectively evolve freely without breaking clients as these are inherently designed with change in mind.

    HATEOAS is nothing more than an abbreviation for hypertext as the engine of application state meaning that the media-type exchanged should allow clients to progress their task without having to contact or lookup external documentation. This is either achieved via attaching URIs to link-relation names, which allows the URI to be swapped over time and clients still being able to lookup the URI to "invoke" via the relation-name, or by returning a representation format that contains elements that "teach" a client on how to construct requests.

    In regards to link-relation names, these should be either based on registered names such as first, last, next, prev or up, or define custom ones following the Web Linking extension mechanism. In the latter case you use a URI that does not necessarily need to point to a resource or documentation, like i.e. https://acme.com/rel/parent. This basically acts as the predicate in a Semantic Web triple which sets the target resource identified by the attached URI in context to the current resource. A URI may even be attached to multiple link-relation names. In case a client does not understand a certain link-relation name it should ignore that relation name. Such relation names may be further defined in media-types or profiles.

    A server can "teach" clients on how to construct requests through form representations or elements defined by a media-type. I.e. think of HTLM form. The HTML form does explain a client how a request sent to the server should look like. It describes the properties a resource supports and the server expects as input along the HTTP method to use upon sending the request, as well as the target URI to send the request to and the representation format to marshal the request to. This is usually implicitly given as application/x-www-form-urlencoded. Elements of a form may even hint a client on what type a property has, its admissible range in terms of numeric values or even allow a client to chose a certain date or time point through various widgets.

    Of course this all depends on the capabilities of the media-type exchanged. I.e. application/json doesn't have these and as such exchanging a resource state in such a representation format will not be very helpful to a client unless it has built-in support for the data returned, which already indicates a tight coupling to the service. As Evert already mentioned hypertext application language (HAL), this JSON based media-type allows servers to teach clients of what URIs are, plain JSON does not have that concept i.e., and allows URIs to be attached to link-relation names. It is therefore a good generic media type for describing general-purpose resources to clients. It though lacks capabilities of teaching clients on how to make form-based requests. Luckily there are either other media-types such as Ion (Amazon Ion) or extensions of HAL, i.e. HAL forms available that close that gap.

    So, while inf3rno has given an example of how you might construct links in your response, the actual representation generated depends on the negotiated media type actually.

    In HAL your JSON payload could be represented like this:

    {
        "nome": "Porto",
        "_links": {
            "self": {
                "href": "/municipios/Porto"
            },
            ...
        },
        "_embedded": {
            "freguesias": {
                "_links": {
                    "self": {
                        "href": "/municipios/Porto/freguesias"
                    },
                    "https://.../rel/parent": {
                        "href": "/municipios/Porto"
                    },
                    "https://.../rel/freguesias/Bonfim": {
                        "href": "/municipios/Porto/freguesias/Bonfim"
                    },
                    "https://.../rel/freguesias/Campanha": {
                        "href": "/municipios/Porto/freguesias/Campanha"
                    },
                    ...
                },
                ...
            }
        }
    }
    

    In Ion the same resource state may look like this:

    {
        "name": "Porto",
        "self": { "href": "/municipios/Porto" },
        "freguesias": {
            "href": "/municipios/Porto/freguesias",
            "value": [
                "https://.../rel/parent": { "href": "/municipios/Porto" },
                "https://.../rel/freguesias/Bonfim": { "href": "/municipios/Porto/freguesias/Bonfim" },
                "https://.../rel/freguesias/Campanha": { "href": "/municipios/Porto/freguesias/Campanha" },
                ...
            ]
        }
    }
    

    Which one you prefer depends on you. In general, the more different media types your applications are able to process the more likely they will be to interoperate with other peers in the network without requiring you to manually touch your application. I.e. you could add support for both HAL and ION and then let the client decide on what representation it prefers through means of content-negotiation.