22

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?

1
  • 1
    It's unclear what you are trying to accomplish, probably because your simple intent is covered beneath the layers of architecture you're putting onto one another, so it's hard to say whether "RESTful APIs" would really help you. From what I understand of your problem, option 2 is a simple and workable solution. The "issue" here is inherent to "RESTful APIs". RestIsJustSqlReinvented and you will indeed run into the same problem when you try to retrieve a sufficiently complex subgraph from any RDBMS. Use a cache or a representation optimized for your queries.
    – back2dos
    Mar 3, 2013 at 20:45

7 Answers 7

5

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.

2
  • Thanks, Mike. I've updated my question to make it a bit more clear. The issue with your answer is that I can't agree a REST client can parse URLs. If it does, then it gets coupled to the URLs. And this violates one of the core ideas of REST: the clients should use rels to pick the links, but should not assume any knowledge of the URLs' structure. REST asserts an API is free to change URLs at will provided that rels remain the same. Parsing URLs shifts us closer to SOAP than to REST. Feb 20, 2013 at 16:38
  • Thank you again. You've described the approach we've taken so far. In a way, we do expose identifiers. The only thing is that we try to expose natural identifiers whenever possible. Feb 26, 2013 at 8:47
6
+50

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

I challenge whether it is worthwhile to differentiate between a REST API and a web application. Your "web application" should just be alternate (HTML) representations of the same resources - which is to say, I don't understand how or why you expect to access http://my.rest.api/... and http://my.web.app/... and that they be simultaneously the same and different.

Your "client" is the browser in this case and it understands HTML and JavaScript. That is the web application in my opinion. Now you may disagree and think that you access said web application using foo.com and expose everything else via api.foo.com - but you have to then ask, how did foo.com provide me the representation of the resource? The "back-end" of foo.com is perfectly capable of understanding how to discover resources from api.foo.com. Your web application has merely become a proxy - no different than if you were talking to another API (from someone else) all together.

So your question can be generalized to, "How can I describe resources using my own URIs that exist in other systems?" which is trivial when you consider that it's not the client (the HTML/JavaScript) that must understand how to do this, but the server. If you agree with my first challenge, then you can simply think of your web application as a separate REST API that proxies or delegates to another REST API.

So when your client accesses my.web.app/pets/1 it knows to present the pet interface because either that's what was returned by the server-side template, or if it's an asynchronous request for some other representation (e.g. JSON or XML), the content-type header tells it so.

The server providing this is the one responsible for understanding what a pet is and how to discover a pet on the remote system. How you do this is up to you - you can simply take the ID and generate another URI, which is what you feel is inappropriate, or you can have your own database that stores the remote URI and proxies the request. Storing this URI is fine - it's equivalent to bookmarking. You'd be doing all this just to have a separate domain name. I don't honestly know why you want this - your REST API URIs have to be bookmark-able, too.

You've already brought up most of this in your question, but I feel you've framed it in a way that really didn't acknowledge that it's the practical way to do what you want to do (based on what I feel is an arbitrary constraint - that the API and the application be separate). By asking if REST APIs can't be back-ends for web applications, and suggesting that performance would be a problem, I think you're focusing all on the wrong things. It's like saying you can't create a Mashup. It's like saying the web doesn't work.

5
  • I don't expect the web app to be simply a representation for the api. It can have lots of differences, e.g. show several child resources together with some root one on a single page. I don't want the web app urls to contain the internal ids of the api data storage, if you mean this by saying that I expect the 2 systems to be the same. I'm not concerned with performance here, it's not the problem. The question is actually 'How do I put 3 into my.web.app/pets/3 without parsing REST API Urls'? Mar 1, 2013 at 13:33
  • Correcting my own re-phrasing: 'How do I put 3 into my.web.app/pets/3 without parsing the corresponding REST API resource's URL my.rest.api/v0/persons/2/pets/3? Or what do I put there?' Mar 1, 2013 at 13:41
  • I think you're confusing the state of the client with the representations that determine that state. You don't put 3 in app/pets/3 because app/pets/3 is opaque, it points to whatever resource your web app wants. If that's a composed view of several other resources (in other systems - your API being one of them) then it's up to you to store the hyperlinks to those systems within the web app server, and then retrieve them, resolve them to their representations (e.g. JSON or XML) and then serve them up as part of your response.
    – Doug
    Mar 1, 2013 at 21:46
  • Think of it this way - forget your API and App. Assume you wanted to create a site that lets people collect their favorite Facebook and Twitter posts. Those are remote systems. You're not going to try to tunnel or template the URIs to those systems through your own. You'd create a 'board' resource and it would be your server that knows that board/1 points to facebook.com/post/123 and twitter.com/status/789 - when you go to provide a representation of your board, you would have to resolve those URIs to a representation you can work with. Cache where needed.
    – Doug
    Mar 1, 2013 at 21:52
  • And so, since you want your API to be significantly different than your app (I still think this is questionable) - treating it like a remote system becomes no different than that. You said performance isn't the problem, but you also said in your question that something like this would 'affect performance'.
    – Doug
    Mar 1, 2013 at 21:54
5

Preface

This answer specifically addresses the question of how to manage your own URL scheme including unique bookmarkable URLs for resources for which the back-end REST API doesn't explicitly expose an identifier, and without interpreting the URLs provided by the API.


Discoverability requires a certain amount of knowledge, so here's my take on a real-world scenario:

Let's say we want a search page at http://my.web.app/person where the results include a link to the details page for each person. The one thing our front-end code must know in order to do anything at all is the base URL for its REST data source: http://my.rest.api/api. The response to a GET request to this URL might be:

<Links>
    <Link ref="self" href="http://my.rest.api/api" />
    <Link rel="person" href="http://my.rest.api/api/person" />
    <Link rel="pet" href="http://my.rest.api/api/pet" />
</Links>

Since our intent is to display a list of people, we next send a GET request to the href from the person link href, which might return:

<Links>
    <Link ref="self" href="http://my.rest.api/api/person" />
    <Link rel="search" href="http://my.rest.api/api/person/search" />
</Links>

We want to display search results, so we'll use the search service by sending a GET request to the search link href, which might return:

<Persons>
    <Person>
        <Links>
            <Link rel="self" href="http://my.rest.api/api/person/1"/>
        </Links>
        <Pets>
            <Link rel="pet" href="http://my.rest.api/api/pet/10"/>
        </Pets>
    </Person>
    <Person>
        <Links>
            <Link rel="self" href="http://my.rest.api/api/person/2"/>
        </Links>
        <Pets>
            <Link rel="pet" href="http://my.rest.api/api/pet/20"/>
        </Pets>
    </Person>
</Persons>

We finally have our results, but how do we build our front-end URLs?

Let's strip off the part that we know for certain: the API base URL, and use the rest as our front-end identifier:

  • known API base: http://my.rest.api/api
  • given URL for individual entity: http://my.rest.api/api/person/1
  • unique id: /person/1
  • our base URL: http://my.web.app
  • our generated front-end URL: http://my.web.app/person/1

Our results might look like:

<ul>
    <li><a href="http://my.web.app/person/1">A person</a></li>
    <li><a href="http://my.web.app/person/2">A person</a></li>
</ul>

Once a user follows that front-end link to the details page, to what URL do we send the GET request for details of that specific person? We know our method for mapping back-end URLs into front-end URLs, so we just reverse it:

  • front-end URL: http://my.web.app/person/1
  • our base URL: http://my.web.app
  • unique id: /person/1
  • known API base: http://my.rest.api/api
  • generated API URL: http://my.rest.api/api/person/1

If the REST API changes such that a person URL is now http://my.rest.api/api/different-person-base/person/1 and someone had previously bookmarked http://my.web.app/person/1, the REST API should (at least for a time) provide backward-compatibility by responding to the old URL with a redirect to the new. All generated front-end links would include the new structure automatically.

As you probably noticed, there are several things we have to know in order to navigate the API:

  • the API base URL
  • the person relation
  • the search relation

I don't think there's anything wrong with this; we're not assuming a specific URL structure at any point, so the structure of the entity URL http://my.rest.api/api/person/1 could change, and as long as the API provides backward-compatibility, our code will still work.


You asked how our routing logic could tell the difference between two front-end URLs:

  • http://my.rest.api/api/person/1
  • http://my.rest.api/api/pet/3.

First I'll point out that you used the API base in your comment when in our example we're using separate base URLs for the UI and REST API. I'm going to continue the example using separate bases, but sharing a base isn't a problem. We can (or should be able to) map UI routing methods using the media type from the request's Accept header.

As for routing to a specific detail page, we can't differentiate those two URLs if we're strict about avoiding any knowledge about the structure of the self URL provided by the API (i.e. the opaque string id). To make this work, let's include another of our known pieces of info - the entity type that we're working with - in our front-end URLs.

Previously our front-end URLs were in the format: ${UI base}/${opaque string id}

The new format could be: ${UI base}/${entity type}/${opaque string id}

So using the /person/1 example, we'd end up with http://my.web.app/person/person/1.

With this format, our UI routing logic would be working with /person/person/1, and knowing that the first token in the string was inserted by ourselves, we can pull it out and route to the appropriate (person, in this example) detail page based on it. If you feel sheepish about that URL, so we could insert a little more in there; maybe: http://my.web.app/person/detail/person/1

In which case we'd parse out the /person/detail for routing and use the rest as the opaque string id.


I think this introduces extremely tight coupling of the web app to the api.

I'm guessing you mean that, since our generated front-end URL contains part of the API URL, if the API URL structure changes without supporting the old structure, we'll need a code change in order to translate the bookmarked URL into the new version of the API URL. In other words, if the REST API changes a resource's ID (the opaque string), then we can't talk to the server about that resource using the old ID. I don't think we can avoid a code change in that situation.

What if I wanted the URL structure for the web app to differ from that of the api?

You can use any URL structure you want. At the end of the day, a bookmarkable URL for a specific resource must include something that you can use to get an API URL that uniquely identifies that resource. If you generate your own identifier and cache it with the API URL as in your approach #3, that will work until someone tries to use that bookmarked URL after that entry is cleared from the cache.

What if my web app's entities did not map to the api entities 1-1?

The answer depends on the relationship. Either way, you would need a way to map front-end to API URLs.

6
  • I have one problem with this approach. It's actually the number 1 in my list of solutions. What I don't get is this: if the web app doesn't interpret the URLs and treats unique ids as opaque strings (just person/1, pet/3), then how would it know that if a browser opens http://my.rest.api/api/person/1 it should show person UI, and if it opens http://my.rest.api/api/pet/3, then pet UI? Feb 26, 2013 at 8:52
  • Good question! I've updated the answer with my response. Feb 26, 2013 at 16:03
  • Thanks, Mike. I think this introduces extremely tight coupling of the web app to the api. What if I wanted the URL structure for the web app to differ from that of the api? What if my web app's entities did not map to the api entities 1-1? I still think that I'd better take the approach of exposing some identifiers, but urging the clients to use links for navigation. Feb 28, 2013 at 9:06
  • This is an interesting topic, so I hope I'm not missing anything. I've updated my answer with responses to your comment. I think exposing some identifiers is a good compromise between complete RESTfulness and usability. Feb 28, 2013 at 20:04
  • My primary concern here is a bit more practical. I use ASP.NET MVC to implement the web app, and due to some internal rules I have to define url patterns the app supports. I.e. if /a/{id} is defined, then the app will handle /a/1, but not /a/1/b/2. This leads to a requirement to recompile the web app if the REST API urls change not only to preserve bookmarked URLs, but also to simply make the web app work when navigated from the root. Simply because hyperlinks embedded into html pages won't work without that. Mar 1, 2013 at 13:23
2

Let's face it, there is no magic solution. Have you read Richardson Maturity Model? It divides REST architecture maturity into 3 levels: Resources, HTTP verbs and hypermedia controls.

I should not expose raw identifiers of my entities, but rather return hyperlinks with rel="self"

This is hypermedia controls. Do you REALLY need it? This approach has some very good benefits (you can read about them here). But there is no such thing as free meals and you will have to work hard (e.g. your second solution) if you want to get them.

It is a question of balance - do you want to sacrifice performance (and make your code more complicated) but get a system that is more flexible? Or do you prefer to keep things faster and simpler but pay later when you introduce changes to your api/model?

As someone who developed a similar system (business logic tier, web tier and web clients) I chose the second option. Since my group developed all the tiers, we decided it is better to have a bit of coupling (by letting the web tier know about entity ids and building api urls) and in return get code which is more simple. Backward compatibility was also not relevant in our case.

If the web application was developed by a 3rd party or if backward compatibility was an issue, we might have chosen differently because then there was great value in being able to change the url structure without changing the web application. Enough to justify complicating the code.

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

I think it means that you don't have to create a perfect REST implementation. You can go with your second solution, or expose entity id's or maybe pass api urls. It's OK as long you understand the implications and trade-offs.

0

I thing if you stick to something similar to the Atom Syndication Format you are good.

Here the metadata describing the Entry being represented can be specified using additional elements/attributes:

  • atom:id As per [RFC4287], contains a URI which uniquely identifies the Entry

  • <atom:link rel="self" href=".." > As per [RFC4287], this element is optional. If included it contains the URI a client should use to retrieve the Entry.

This is just my two cents.

1
  • Maybe I don't get something, but it seems to me that your answer does not explain how to generate URLs of a web app that is a client to a REST API, does it? Feb 28, 2013 at 9:08
0

Don't worry about the URLs, worry about the media types.

See here (third bullet point in particular).

A REST API should spend almost all of its descriptive effort in defining the media type(s) used for representing resources and driving application state, or in defining extended relation names and/or hypertext-enabled mark-up for existing standard media types...


In the case of a typical web app, the client is a human; the browser is just an agent.

So an anchor tag like

          <a href="example.com/foo/123">click here</a>

corresponds to something like

          <link type="text/html" rel="self" href="example.com/foo/123">

The URL is still opaque to the user, all she cares about are the media types (e.g. text/html, application/pdf, application/flv, video/x-flv, image/jpeg, image/funny-cat-picture etc). The descriptive text contained in the anchor (and in the title attribute) is just a way of extending the relationship type in a way that is intelligible to humans.

The reason you want the URI to be opaque to clients is so that you reduce coupling (one of the primary goals of REST). The server can change/reorganize the URIs without affecting the client (as long as you have a good caching policy -- which might mean no caching at all).

In Summary

Just make sure the client (human or machine) cares about the media types and the relations rather than the URLs and you'll be fine.

3
  • Rodrick, my question is not about building the API, but rather about building a web application that sits on top of a RESTful API. I can hardly understand how media types can help me build URLs for the web app. Although media types are crucial for service contract and discoverability. Feb 28, 2013 at 9:10
  • @PavelGatilov - Is the client of your web app a human? Feb 28, 2013 at 13:21
  • Yes, it is. And a very underskilled one. Mar 1, 2013 at 5:17
0

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.

I think you're right, that is the simplest way. You can relativise the URLs against http://my.rest.api/api to make them less ugly:

http://my.web.app/person/person%2F1234

If the URL provided by the API doesn't happen to be relative to that base, it degrades to the ugly form:

http://my.web.app/person/http%3A%2F%2Fother.api.host%2Fapi%2Fperson%2F1234

To take it a step further, inspect the response from the API server to determine what kind of view you want to present and stop encoding the path segment delimiter and colons:

http://my.web.app/person/1234 (best case)
http://my.web.app/http://other.api.host/api/person/1234 (ugly case)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.