2

Context

Let's say I have the API endpoints /api, /api/accounts and /api/accounts/{id}.

As far as I understand, the idea is that a call to /api gives me a list of "base" API calls, among which is /api/accounts. Calling that will give me a list of entities, with each providing a follow-up link to the respective /api/accounts/{id}. So far, so good.

Now I build a website to use that API, with pages under /ui/see-accounts and /ui/view-account/{id}.

Problem

The page under /ui/see-accounts has all those follow-up links from /api/accounts. I have a ready-for-use href to /api/accounts/123, but that's not where I want to send the user. I want to send them to /ui/view-accounts/123. That page is supposed to then call /api/accounts/123.

Attempt A

Pass the href /api/accounts/123 to the next page.
Now it doesn't seem right that I use the same information (account id) twice, but the one visible to the user is actually irrelevant. (It will get data from /api/accounts/123, but the browser could just as well say /ui/view-account/456 in the address bar).

Furthermore, a user expects it to work if they send somebody a direct link to /ui/view-account/123.

Attempt B

Add the single-account-endpoint template to the "base" calls
By adding a URI template for /api/accounts/{id} to the links given under /api, the page /ui/view-account/123 knows how to call the API without coupling the frontend to the hardcoded URL.

However, now the links provided by /api/accounts aren't used.

Question

I feel like my solutions go against the spirit of HATEOAS. Are there better ways to resolve these, or is this simply the consequence of using a "real" frontend (opposed to click-navigating the API in a browser)?

3
  • Assuming that you want to follow HATEOAS (which isn't necessarily something that you want to do), consider what the browser does (think of the browser for the moment as of an application client, not as of a platform). It asks for a resource, it gets back a web page with some markup that it then interprets/renders. Some of that markup contains links, but not all of those links (in the generalized sense) are meant to be clickable. Some are, some require input (forms), and some are automatic. 1/3 – Filip Milovanović Mar 6 at 17:58
  • The browser also needs to fetch data (css, scripts, images, etc.), and the information about that is embedded into the response, and the browser (being a client of a web application) knows how to interpret those bits of information because both the browser and the application rely on the same contract - the HTTP + HTML combo. 2/3 – Filip Milovanović Mar 6 at 17:58
  • Now, your application uses the web as a platform, but it can also define an api/protocol/contract, interpret the response, and perform automatic calls where required. But again, while HATEOAS might be something you want to pursue, remember that in REST (as defined in Roy Fielding's dissertation, not the industry), HATEOAS is part of a design motivated by the requirements/constraints of internet-scale systems (huge heterogeneous networks). 3/3 – Filip Milovanović Mar 6 at 17:58
2

REST dissociates resources from their representations. You are trying to do the opposite.

I mean when I call GET /account/1 with Accept-Type: text/html, I would expect to receive an HTML representation. If I call that same thing with Accept-Type: application/vnd.something.account+json, I would expect to receive a json-based representation, probably for automated consumption.

The thing is, there should be only one /account/1. That is the resource for account #1. There shouldn't be another resource with the same semantics just because there's another representation.

As a slightly worse solution, you could do Attempt A, essentially keeping a "shadow state" on the client. I see no problems with that other than the above.

Attempt B seems even worse than "A". A RESTful client should generally not know how to construct URIs for the server. It should receive all URIs as links. Dynamic data (like skipping to a page or similar) should go through forms, if needed, which essentially lets the server construct the link for you.

A RESTful HTTP "API" is already a web-application if done right, so there's no need to add a frontend web-application on top of it.

5
  • I feel like we are not on the same page about the problem. The URLs were chosen to make it easier to talk about, but they are irrelevant to the question at hand: If I put it under GET /account/1 with Accept-Type: text/html, there's still code there that needs to run, and needs to get the data from the API - so, code that somehow needs to know which URL to call. When I click on the link to /account/1 in the browser, how does the code behind that page know which API endpoint to call? – R. Schmitz Mar 6 at 7:42
  • The resource can present itself in HTML, or in any representation it supports. It doesn't need to call "the API", it is the API. I completely get, that is not how most projects think about it, but that is how it is supposed to work. The only reason to not do the above, if the "API" is not RESTful (is RPC and/or CRUD), or just badly written, and/or you have no control over it (is external), in which cases I would go with A. – Robert Bräutigam Mar 6 at 11:16
  • Well, I'm only learning, so I'll presume that's indeed how it is. But... why an API at all then? It seems EITHER you're a human and should use GET /account/1 with Accept-Type: text/html. OR you're an application, but you shouldn't be, because the resource presents itself. Is "REST API" an oxymoron (or just something nobody would/should use)? – R. Schmitz Mar 7 at 12:57
  • A REST API may be needed if you don't control the clients, don't entirely control the use-case, or have some architectural constraints that forced it on you. Building a REST API just for your own web-frontend seems pretty redundant to me. It just makes things more complicated and more difficult to maintain. Since now you'll have to maintain both sides and a protocol too when something changes. Why is it seemingly so popular? Maybe hype? I don't know for sure. – Robert Bräutigam Mar 7 at 18:37
  • "Why is it seemingly so popular?" - I assume it's that it's not "full" REST in most cases. For example, with Richardson Maturity Model, up to level 2 is a pretty good structure for such APIs. And if you are working with this, you don't want to say the full specification "Our REST API at Richardson Level 2", but rather just "Our REST API". – R. Schmitz Mar 16 at 20:46
2

It sounds like you are trying to work out a navigation scheme for your single page Javascript app.

Why not keep it simple.

If the user goes to /accounts in the browser that page loads /accounts from the back-end API (I assume this is some kind of AJAX call to fetch JSON or XML data) and presents it to the user in what ever nice format the single page JS app is taking care of.

It doesn't seem to really serve any purpose to have different phrasing in your back-end URI scheme and your single page JS apps navigation scheme. The navigation URIs of a single page JS app really have nothing to do with the URIs of any API that single page app calls. You don't need to put ui in one schema and api in another since they are totally different things. One is for the navigation of the single page app and the other is the schema of the back-end server, they can have nothing to do with each other.

Of course you can make it work the way you described, and the routing logic of what ever JS framework you are using should handle this.

So if the user goes to

/ui/see-accounts

and your JavaScript app fetches a lot of Account resources from the back-end (in JSON say) from /api/accounts, your framework should provide a way to generate navigation links for those resources based on just their ID and what ever scheme you are using for navigation.

So

/api/accounts/123

can become

/ui/view-account/123

when you display these links to the user (its JavaScript after all, you can basically do what ever you like with the JSON data you fetch from the back-end

Again your framework should make this trivial to do. But then its also trivial not to and it really doesn't give you any advantage

The only use case where I've had to do this is when the URI scheme for the back-end actually changed, so the HTTP calls to the back-end all had new URIs, but we wanted to keep the navigation scheme of the Ember app the same.

3
  • Is the idea here to parse the link provided by the backend (/api/accounts/123) in order to find the id and then create /ui/view-account/123 from it? Strikes me as odd, but wouldn't change the question: /ui/view-account/123 still needs to somehow know to call /api/accounts/123 to get the data - how will that page know that though? – R. Schmitz Mar 6 at 7:51
  • Well you probably wouldn't parse the link itself, but rather the data structure that contains the link. So if you GET /api/accounts you might get back something like [{id: 123, name: Blackburn Account, uri: /api/accounts/123} ... ] and your Javascript app can take that JSON objects and construct a link for each obj in that collection using the different URI schema. How it knows how to do this will depend on the single page Javascript app framework you are using. In most frameworks fetching data from back-end is completely different to navigation routing – Cormac Mulhall Mar 6 at 14:37
  • Then when you go to /ui/view-account/123 your JavaScript also knows that on that page it should load account data for that specific account via the back-end URL /api/accounts/123. You construct the back-end URI using the ID of the account you care about, parsed from the navigation URI. It knows how to do this because you program it to do this or use features of what ever framework you are using – Cormac Mulhall Mar 6 at 14:44

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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