Rest – A Web application as a REST API client: how to handle resource identifiers

restweb-applications

Several concepts related to REST conflict in my head when I try implementing it.

I have a REST-ful back-end API system that holds the business logic, and a web application that provides the UI. From various resources about REST (particularly, REST in Practice: Hypermedia and Systems Architecture) I know that I should not expose raw identifiers of my entities, but rather return hyperlinks with rel="self".

Consider the example. The REST api has a resource that returns a person:

<Person>
  <Links>
    <Link rel="self" href="http://my.rest.api/api/person/1234"/>
  </Links>
  <Pets>
    <Link rel="pet" href="http://my.rest.api/api/pet/678"/>
  </Pets>
</Person>

The problem arises with the web application. Let's assume it returns a page that contains a hyperlink to browsers:

<body class="person">
  <p>
    <a href="http://my.web.app/pet/???????" />
  </p>
</body>

What should I put into the href attribute? How do I keep the API entity URL in the web application to be able to get the entity when a user opens the target page?

The requirements seem conflicting:

  1. The hyperlink href should lead to the web application because it is the system hosting the UI
  2. The href should have some id of the entity because the web app must be able to address the entity when the target page opens
  3. The web app should not parse/construct REST URLs beacuse it's not REST-ful, The mentioned book says

URIs should be opaque to consumers. Only the issuer of the URI knows how to interpret it and map it to a resource.

So, I cannot just take 1234 from the API response URL because as a RESTful client I should treat it as if it was something like http://my.rest.api/api/AGRIDd~ryPQZ^$RjEL0j. On the other hand, I must give some URL that leads to my web app and is enough for the app to somehow restore the API's original URL and use that URL to access the API resources.

The simplest way is probably just using the API URLs of resources as their string identifiers. But web page urls like http://my.web.app/person/http%3A%2F%2Fmy.rest.api%2Fapi%2Fperson%2F1234 are ugly.

It all seems quite easy for a desktop app or a single-page javascript app. Since they live continuously, they can just keep the URLs in memory together with the service objects for the application lifetime and use them when necessary.

With a web app I can imagine several approaches, but all seem weird:

  1. Replace the host in the API URLs and keep the result only. The huge downside is that it requires the web application to handle whatever URL the API generates, meaning monstrous coupling. Moreover, it's not RESTful again, because my web app starts interpreting the URLs.
  2. Expose the raw ids in the REST API together with the links, use them to build Web App's URLs, and then use the ids on the web app server to find the required resources in the API. This is better, but will affect the web app server performance because the web app will have to go through the REST service navigation issuing a chain of get-by-id requests of some form to handle any request from a browser. For a somewhat nested resource this might be costly.
  3. Store all self URLs returned by the api in a persistent (DB?) mapping on the web app server. Generate some ids for them, use the ids to build the web app page URLs and to get the URLs of the REST service resources. I.e. I keep the http://my.rest.api/pet/678 URL somewhere with a new key, say 3, and generate the web page URL as http://my.web.app/pet/3. This looks like a HTTP Cache implementation of some sort. I don't know why, but it seems weird to me.

Or Does it all mean that RESTful APIs cannot serve as backends for web applications?

Best Answer

Edited to address question updates, previous answer removed

Looking over your changes to your question I think I understand the problem you are facing a bit more. As there is no field that is an identifier on your resources (just a link) you have no way to refer to that specific resource within your GUI (i.e. a link to a page describing a specific pet).

The first thing to determine is if a pet ever makes sense without an owner. If we can have a pet without any owner then I would say we need some sort of unique property on the pet that we can use to refer to it. I do not believe this would violate not exposing the ID directly as the actual resource ID would still be tucked away in a link that the REST client wouldn't parse. With that in mind our pet resource may look like:

<Entity type="Pet">
    <Link rel="self" href="http://example.com/pets/1" />
    <Link rel="owner" href="http://example.com/people/1" />
    <UniqueName>Spot</UniqueName>
</Entity>

We can now update the name of that pet from Spot to Fido without having to mess with any actually resource IDs throughout the application. Likewise we can refer to that pet in our GUI with something like:

http://example.com/GUI/pets/Spot

If the pet does not make any sense without an owner (or pets are not allowed in the system without an owner) then we can use the owner as part of the "identity" of the pet in the system:

http://example.com/GUI/owners/John/pets/1 (first pet in the list for John)

One small note, if both Pets and People can exist separate of each-other I would not make the entry point for the API the "People" resource. Instead I would create a more generic resource that would contain a link to People and Pets. It could return a resource that looks like:

<Entity type="ResourceList">
    <Link rel="people" href="http://example.com/api/people" />
    <Link rel="pets" href="http://example.com/api/pets" />
</Entity>

So by only knowing the first entry point into the API and not processing any of the URLs to figure out system identifiers we can do something like this:

User logs into the application. The REST client accesses the entire list of people resources available which may look like:

<Entity type="Person">
    <Link rel="self" href="http://example.com/api/people/1" />
    <Pets>
        <Link rel="pet" href="http://example.com/api/pets/1" />
        <Link rel="pet" href="http://example.com/api/pets/2" />
    </Pets>
    <UniqueName>John</UniqueName>
</Entity>
<Entity type="Person">
    <Link rel="self" href="http://example.com/api/people/2" />
    <Pets>
        <Link rel="pet" href="http://example.com/api/pets/3" />
    </Pets>
    <UniqueName>Jane</UniqueName>
</Entity>

The GUI would loop through each resource and print out a list item for each person using the UniqueName as the "id":

<a href="http://example.com/gui/people/1">John</a>
<a href="http://example.com/gui/people/2">Jane</a>

While doing this it could also process each link that it finds with a rel of "pet" and get the pet resource such as:

<Entity type="Pet">
    <Link rel="self" href="http://example.com/api/pets/1" />
    <Link rel="owner" href="http://example.com/api/people/1" />
    <UniqueName>Spot</UniqueName>
</Entity>

Using this it can print a link such as:

<!-- Assumes that a pet can exist without an owner -->
<a href="http://example.com/gui/pets/Spot">Spot</a>

or

<!-- Assumes that a pet MUST have an owner -->
<a href="http://example.com/gui/people/John/pets/Spot">Spot</a>

If we go with the first link and assume that our entry resource has a link with a relation of "pets" the control flow would go something like this in the GUI:

  1. Page is opened and the pet Spot is requested.
  2. Load the list of resources from the API entry point.
  3. Load the resource that is related with the term "pets".
  4. Look through each resource from the "pets" response and find one that matches Spot.
  5. Display the information for spot.

Using the second link would be a similar chain of events with the exception being that People is the entry point to the API and we would first get a list of all people in the system, find the one that matches, then find all pets that belong to that person (using the rel tag again) and find the one that is named Spot so we can display the specific information related to it.

Related Topic