How can I avoid hardcoding URLs in a RESTful client/server web app with deep linking? - rest

I'm working on a SPA which is a client to a RESTful web service. Both the client and server are part of the same project, i.e. I can modify the code for both sides freely. I've been reading up on RESTful API design to try and make sure I'm doing everything the "right" way. One of my takeaways from reading is that a RESTful service should publish hyperlinks so clients can access more information, and that clients should have no hardcoded information about service URLs other than an entry point. Using hyperlinks allows the client to be more flexible in the event that the server makes URL changes.
However I can't figure out how this architecture is supposed to work when users are allowed to link to a specific client state. For example:
One of the views is a list of books available for purchase. The client sets the browser's location to /books/ to identify this page, and the backend data comes from an endpoint /api/books/, retrieved from an API entry point that publishes that URL. The service URL responds with a JSON document like this:
[
{"title": "The Great Gatsby",
"id": 24,
"url": "http://localhost/api/books/24/"},
< and so on >
]
The client uses this to generate readable links that, when clicked, go to a detailed view of a single book. The browser's location is updated to /books/the-great-gatsby/24/ so users can bookmark this view and link to it.
How does the client handle when users click this link directly?? How would it know where to get the information for this book without having a hardcoded URL?
The best I could come up with is the following sequence of requests:
GET /api/ - view which services are available (to find there are books at all)
OPTIONS /api/books/ - view a description of what operations are available on books (so e.g. it can make sure it can find books by ID)
GET /api/books/?id=24 - See if it can find a book with an ID that matches the ID in the browser's location.
GET /api/books/24/ - Actually retrieve the data
Anything shorter would imply that the client has hardcoded knowledge of the API's URLs. However, from a web app point of view, this seems grossly inefficient.
Is there some trick I'm missing? Is there a way for the client to "know" how to get more detail about book ID 24 without somehow having the /api/books/24/ endpoint hardcoded?

if you request this resource /books/the-great-gatsby/24/ from the server, the server should respond with something specific to that URL. Currently, you are probably analyzing window.location which is a bit of a hack.
If /books/the-great-gatsby/24/ is static content, then you have very little choice: You store the client's current state explicitly somewhere (i.e. /books?data=api/books/24 or implicitly /books/the-great-gatsby/24/ which then leads to the client having to know how to translate that to an API resource.
The RESTful way is to use hypertext to indicate where any related resources (i.e. your data to render is) are which makes a tag an appropriate choice.
i.e. ditch the static content, and render /books/the-great-gatsby/24/ with a <head><link href="api/books/24" ....></link></head>
However, if you always retain control of your client side and don't plan to publish the API to third parties, you might be more productive ditching RESTful and just go RESTish.

The Resource URL Locator pattern
In this answer: user is (the human interacting with) the internet browser, client is the Single Page Application (SPA) and server is the REST API.
Deep linking is a convenience of the client to the user; the client itself may still not have knowledge of the server's URLs, so the client must start at the root URL of the server. The client uses content negotiation to indicate which media type it needs. The first request of the client to the server when bootstrapping itself could be as follows:
GET /?id=24 HTTP/1.1
Accept: application/vnd.company.book+json
Optionally, the client uses the id querystring parameter as a hint to the server to select the specific resource it is looking for.
When the server has determined which resource the client is looking for it can respond with a redirect to the canonical URL of the resource:
HTTP/1.1 303 See Other
Location: https://example.com/api/books/24
The client can now follow the redirect and get the resource it needs to bootstrap the application.

#Evert's comment got me thinking. Isn't a deep link or a bookmark just a continuation of application state from a previous point in time? It doesn't really matter how much time has passed after the previous application state transition.
You could say that the 'current' application state in HATEOAS is the last followed link. The current state must be stored somewhere and it might as well be stored in the application URL.
Starting the application at a deep link indicates to the application that it should rebuild the application state by requesting the resource indicated by the application URL. If the resource is no longer available or has moved, the server should respond with a 404 Not Found or 301 Moved Permanently respectively.
With this approach the server is still in control of the URLs. The application follows the hypermedia links in the server's responses and doesn't generate URLs itself.

Related

SPA Routing with a RESTful API using HATEOAS

When using routing in a SPA web app (angular, react, etc), the user doesn't have to start at the entry point of the application. They can use a URL in the browser to drill down into any part of the application.
When implementing HATEOAS in a RESTful backend API, we assume that the front-end only knows the URL to the entry point of the API, and then the API provides links to other parts of the application from there.
So this begs the question, if a user enters a URL in the browser that loads a specific part of the SPA (not the entry point), how does the SPA get the appropriate API link needed for just that part of the SPA?
Does the SPA just make a bunch of API calls all at once, starting at the entry point of the API and following links until it gets the link it needs for the state it needs to load? And what happens when the API does not include the link needed because it's not a valid link based on the current state of the application?
HATEOAS doesn't seem to be very compatible with a modern SPA where you can load the application at very specific sections/states.
This has been asked on SO before, but I'm having trouble finding these past questions and answers. I've answered at least two, but I'll summarize how this usually gets answered.
Camp 1: Don't do SPA's, it doesn't make sense. Your HATEOAS service should serve the entire state in a way that the client can interpret and SPA's are basically a hack.
Camp 2 (the camp I'm in): You'll need some way to map SPA links to links on your backend.
You're kind of describing this by talking about 'all the steps needed' to get to the right resource, but if your goal it ultimately map this to an API uri, why not embed just that
https://spa.example/http://api.example/some/resource
Having bookmarkable urls that contain the full path to your backend, means you don't need to jump through complex hoops, and your SPA is a true client for the API you are building.
Another nice benefit is that you can also easily point your client to multiple live/dev environments.
Lastly, you could allow for relative urls and automatically resolve them to the API url, shortening:
https://spa.example/http://api.example/some/resource
to
https://spa.example/some/resource

REST API design for resource modification: catch all POST vs multiple endpoints

I'm trying to figure out best or common practices for API design.
My concern is basically this:
PUT /users/:id
In my view this endpoint could by used for a wide array of functions.
I would use it to change the user name or profile, but what about ex, resetting a password?
From a "model" point of view, that could be flag, a property of the user, so it would "work" to send a modification.
But I would expect more something like
POST /users/:id/reset_password
But that means that almost for each modification I could create a different endpoint according to the meaning of the modification, i.e
POST /users/:id/enable
POST /users/:id/birthday
...
or even
GET /user/:id/birthday
compared to simply
GET /users/:id
So basically I don't understand when to stop using a single POST/GET and creating instead different endpoints.
It looks to me as a simple matter of choice, I just want to know if there is some standard way of doing this or some guideline. After reading and looking at example I'm still not really sure.
Disclaimer: In a lot of cases, people ask about REST when what they really want is an HTTP compliant RPC design with pretty URLs. In what follows, I'm answering about REST.
In my view this endpoint could by used for a wide array of functions. I would use it to change the user name or profile, but what about ex, resetting a password?
Sure, why not?
I don't understand when to stop using a single POST/GET and creating instead different endpoints.
A really good starting point is Jim Webber's talk Domain Driven Design for RESTful systems.
First key idea - your resources are not your domain model entities. Your REST API is really a facade in front of your domain model, which supports the illusion that you are just a website.
So your resources are analogous to documents that represent information. The URI identifies the document.
Second key idea - that URI is used by clients to cache representations of the resource, so that we don't need to send requests back to the server all the time. Instead, we have built into HTTP a bunch of standard ways for communicating caching meta data from the server to the client.
Critical to that is the rule for cache invalidation: a successful unsafe request invalidates previously cached representations of the same resource (ie, the same URI).
So the general rule is, if the client is going to do something that will modify a resource they have already cached, then we want the modification request to go to that same URI.
Your REST API is a facade to make your domain model look like a web site. So if we think about how we might build a web site to do the same thing, it can give us insights to how we arrange our resources.
So to borrow your example, we might have a web page representation of the user. If we were going to allow the client to modify that page, then we might think through a bunch of use cases (enable, change birthday, change name, reset password). For each of these supported cases, we would have a link to a task-specific form. Each of those forms would have fields allowing the client to describe the change, and a url in the form action to decide where the form gets submitted.
Since what the client is trying to achieve is to modify the profile page itself, we would have each of those forms submit back to the profile page URI, so that the client would know to invalidate the previously cached representations if the request were successful.
So your resource identifiers might look like:
/users/:id
/users/:id/forms/enable
/users/:id/forms/changeName
/users/:id/forms/changeBirthday
/users/:id/forms/resetPassword
Where each of the forms submits its information to /users/:id.
That does mean, in your implementation, you are probably going to end up with a lot of different requests routed to the same handler, and so you may need to disambiguate them there.

How to make initial request for nested resource from self describing REST API

Background:
I have a single page application that pulls data from a REST API. The API is designed such that the only URL necessary is the API root, ie https://example.com/api which provides URLs for other resources so that the client doesn't need to have any knowledge of how they are constructed.
API Design
The API has three main classes of data:
Module: Top level container
Category: A sub-container in a specific module
Resource: An item in a category
SPA Design
The app consuming the API has views for listing modules, viewing a particular module's details, and viewing a particular resource. The way the app works is it keeps all loaded data in a store. This store is persistent until the page is closed/refreshed.
The Problem:
My question is, if the user has navigated to a resource's detail view (example.com/resources/1/) and then they refresh the page, how do I load that particular resource without knowing its URL for the API?
Potential Solutions:
Hardcode URLs
Hardcoding the URLs would be fairly straightforward since I control both the API and the client, but I would really prefer to stick to a self describing API where the client doesn't need to know about the URLs.
Recursive Fetch
I could fetch the data recursively. For example, if the user requests a Resource with a particular ID, I could perform the following steps.
Fetch all the modules.
For each module, fetch its categories
Find the category that contains the requested resource and fetch the requested resource's details.
My concern with this is that I would be making a lot of unnecessary requests. If we have 100 modules but the user is only ever going to view 1 of them, we still make 100 requests to get the categories in each module.
Descriptive URLs
If I nested URLs like example.com/modules/123/categories/456/resources/789/, then I could do 3 simple lookups since I could avoid searching through the received data. The issue with this approach is that the URLs quickly become unwieldy, especially if I also wanted to include a slug for each resource. However, since this approach allows me to avoid hardcoding URLs and avoid making unnecessary network requests, it is currently my preferred option.
Notes:
I control both the client application and the API, so I can make changes in either place.
I am open to redesigning the API if necessary
Any ideas for how to address this issue would by greatly appreciated.
Expanding on my comment in an answer.
I think this is a very common problem and one I've struggled with myself. I don't think Nicholas Shanks's answer truly solves this.
This section in particular I take some issues with:
The user reloading example.com/resources/1/ is simply re-affirming the current application state, and the client does not need to do any API traversal to get back here.
Your client application should know the current URL, but that URL is saved on the client machine (in RAM, or disk cache, or a history file, etc.)
The implication I take from this, is that urls on your application are only valid for the life-time of the history file or disk cache, and cannot be shared with other users.
If that is good enough for your use-case, then this is probably the simplest, but I feel that there's a lot of cases where this is not true. The most obvious one indeed being the ability to share urls from the frontend-application.
To solve this, I would sum the issue up as:
You need to be able to statelessly map a url from a frontend to an API
The simplest, but incorrect way might simply be to map a API url such as:
http://api.example.org/resources/1
Directly to url such as:
http://frontend.example.org/resources/1
The issue I have with this, is that there's an implication that /resource/1 is taken from the frontend url and just added on to the api url. This is not something we're supposed to do, because it means we can't really evolve this api. If the server decides to link to a different server for example, the urls break.
Another option is that you generate uris such as:
http://frontend.example.org/http://api.example.org/resources/1
http://frontend.example.org/?uri=http://api.example.org/resources/1
I personally don't think this is too crazy. It does mean that the frontend needs to be able to load that uri and figure out what 'view' to load for the backend uri.
A third possibility is that you add another api that can:
Generate short strings that the frontend can use as unique ids (http://frontend.example.org/[short-string])
This api would return some document to the frontend that informs what view to load and what the (last known) API uri was.
None of these ideas sound super great to me. I want a better solution to this problem, but these are things I came up with as I was contemplating this.
Super curious if there's better ideas out there!
The current URL that the user is viewing, and the steps it took to get to the current place, are both application state (in the HATEOAS sense).
The user reloading example.com/resources/1/ is simply re-affirming the current application state, and the client does not need to do any API traversal to get back here.
Your client application should know the current URL, but that URL is saved on the client machine (in RAM, or disk cache, or a history file, etc.)
The starting point of the API is (well, can be) compiled-in to your client. Commpiled-in URLs are what couple the client to the server, not URLs that the user has visited during use of the client, including the current URL.
Your question, "For example, if the user requests a Resource with a particular ID", indicates that you have not grasped the decoupling that HATEOAS provides.
The user NEVER asks for a resource with such-and-such an ID. The user can click a link to get a query form, and then the server provides a form that generates requests to /collection/{id}. (In HTML, this is only possible for query strings, not path components, but other hypermedia formats don't have this limitation).
When the user submits the form with the ID number in the field, the client can build the request URL from the data supplied by the server+user.

RESTful web service - HATEOAS

I have created a quite simple RESTful web service. It only supports the GET (=read) method, e.g.:
http://localhost/application/id/xyz
The corresponding information for this ID is queried from a data source and returned as JSON.
Now my question: (How) should I implement HATEOAS in this case? Does it even make sense? I understand that HATEOAS is reasonable when having a more complex structure. But in this case, there are no other resources I could link to. The client calls the web service with a certain ID and the server returns information.
Thank you!
As you've said "The client calls the web service with a certain ID" it sounds like you've written your client to visit a specific URL in your service which has the URL to visit generated by the client, i.e. your client application already knows it can visit http://localhost/application/id/xyz for the xyz ID.
If you'd like to leverage some of the power of HATEOAS and decouple yourself from this (slight) dependency, you could instead be querying http://localhost/application/id?query=xyz which could return a list of valid links (if any exist). That way you could change the format or structure of the linked URL without issues for your client (of course, you'd still be dependent on the query URL in some way).
However, as your usage is so simple, this sounds like overkill and unnecessary work so I'd suggest you don't need to worry about HATEOAS until you have a more complex system or clients :)
In HATEOS your return value is not an ID but a URL. Invoking that URL links you to the next resource in the web. Just like a web page containing links to other web pages.

RESTful client - how to process an external link?

By RESTful best services there is the HATEOAS principle which told us that we should not allow the client to build resource URL-s. If we follow this principle, it will be pretty hard to share the current state of the client. For example if you have a REST service on the server, and you gets data via AJAX with a single page javascript client, then you will have 2 urls. One for the client state, and one for the result you got from the REST service. You can share only the client state with the use due to pushState... If somebody runs the client with a previously shared url, then her client won't know about the url of the REST service it should call, because the client cannot build URL-s, just receive from the REST service and utilize it.
For example:
I browse the http://my.client.com
the page gets the root resource from the http://my.api.com, and return a link
the link contains the http://my.api.com/users url, with rel user collection
after that the client displays a button with label: userlist
I click on that button, the client get the data from the api, and prints the user list
if I want to share the user list with my girlfriend, then I have to change the browser url from the client with pushState, for example from http://my.client.com to http://my.client.com/users
after that I send that url to my girlfriend
she copy-pastes that into her browsers address bar and presses enter. after that the client says a huge wtf, because - like John Snow - it knows nothing about what state that url means...
This problem can be solved, if we allow the client to build GET http://my.api.com/users from the url: http://my.client.com/users, but this won't be RESTful, because the client should not build api urls...
If I want to display a nested menu in the client, then that is another problem, because I don't want to send the whole menu tree in every answer. I could create a menu projection for every resource, or use the OPTIONS method, or a custom method to send that data, but that would be a pain in the back. This can be solved by following the rel=up links - got from the REST service - in series, but if I don't know from where should I follow, it will not work...
This problem occurs by google bots too...
How can I both solve this problem, and stay inside the boundaries of the HATEOAS principle?
Normally we don't want to share all of that information with anybody, so we cannot export all of that just the current page we are in.
There is nothing wrong with storing the whole resource on the client and then pushing it up to the server to change the state on the server. If you are worried your resources are getting too large though you could break the resources out a bit. So say you have an order resource and that needs to associate with an address. You don't need to put the address in the order resource, just a link to the address to use. The user can add or alter that address independently. So you might have something like
www.myapi.com/users/1234/shippingaddresses/default
And the client can PUT a new address to this resource. Then in the body of the order resource you can have a link to this resource
POST www.myapi.com/users/1234/orders
{
...order information...
"shipping_address": "www.myapi.com/users/1234/shippingaddresses/default"
}
To be RESTful the client should not build that URL, it should have been given it by the server at some point in the recent past, possibly when the users is selecting which address to use. For example, in the previous step the client could have requested all addresses
GET www.myapi.com/users/1234/shippingaddresses
And presented the list of addresses to the user in a drop down list.