1

Let's assume I have a batch job that needs to print orders. It will do so by getting orders from the order service and send them to the print service.

It uses HATEOAS to discover the link from the root on the order service to get the orders that need to be printed:

GET https://orderservice/

{
  _links: {
    ordersToPrint: "https://orderservice/toprint"
  }
}

The same goes for the print service:

GET https://printservice/

{
  _links: {
    printOrder: "https://printservice/order"
  }
}

The batch job needs to know up front that the names of the links are ordersToPrint and printOrder, because no human interaction will take place during the execution of the job, so there is no "brain" to realize which of the links returned should be the ones to follow. This means that the batch job is now tightly coupled to these names instead of the actual URLs like it would be in a non-HATEOAS approach.

We have now exchanged the tight coupling to URLs with a tight coupling to names.

A couple things, I've come up with which might go against HATEOAS:

  1. URLs might change
    • We could just use a reverse proxy which will redirect from the old to the new URL
  2. The client (the batch job) might no longer have sufficient rights to retrieve orders in which case the HATEOAS API would simply no longer return the "ordersToPrint" URL.
    • In a UI this might mage sense, because a link to get those orders would not be shown to the user. However if the job does not see the link name that it needs, it would crash which is the same as if it just tried to call that URL directly in a non-HATEOAS scenario.

Is there any other reason to use HEATEOAS in this scenario?

6
  • Sharing links tell the job what can be done based on the provided links. Say a second job with a different goal should not be aware of other resources/actions related to orders. You also reduce the number of URL set in config files (say you have 100 jobs each of which uses 2-3 urls that means 200-300 properties to maintain). Descriptive response model also ease the creation of clients via auto-generated code (as we often do with OpenAPI). It can also ease job orchestration by sharing via link the next jobs to trigger, so on and so forth... There are as many advantages as you want to see
    – Laiv
    Oct 12, 2023 at 11:00
  • If there are more links than the job needs, it makes no difference for the job. If there are less links than the job expects, it would either crash, because the link is not there (using HATEOAS), or it would crash when calling the URL (hardcoding URLs). So that does not make any difference either. For the number of properties to maintain, it does also not make any difference if I need to maintain 200 names or 200 URLs. For auto generated clients, I'd argue that OpenAPI is probably the better tooling here, because it does not need to execute anything on the API itself. For sharing links I agree
    – wertzui
    Oct 12, 2023 at 11:22
  • As I said, There are as many advantages as you want to see. We'll see more or less depending on the context and how biased we are by our common practices. For example, I would argue that REST is the best way for a job to call a remote procedure such as printing.
    – Laiv
    Oct 12, 2023 at 11:53
  • An interesting one I found was that the whole authorization logic can be placed in the backend. So if a use has a right to view the link will be available, otherwise not. If a user is allowed to print, link visible otherwise not. So your frontend is not aware of which urls may or may not be accessible. Same for logic as: url to complete order is only shown when the order is picked. Oct 12, 2023 at 12:48
  • @LucFranken this is 2. from my initial question. I get that this totally makes sense for a UI that is presented to a human. However a batch job can just fail if one of the links it needs is not present. It cannot make an educated guess what to do instead.
    – wertzui
    Oct 12, 2023 at 12:53

3 Answers 3

2

needs to know up front that the names of the links are ordersToPrint and printOrder

Yes, that's right. And it is a good thing.

It's a level of indirection. You offered relatively boring "toprint" and "order" URLs, which is fine. But some requests might show they are "toprint_v2", "order_v3", or they might have version identifiers further up in the pathname.

If revving versions goes smoothly, the batch job reads those URLs and automagically does the Right Thing. If there's trouble, the batch job or another client might choose to use a deliberately down-rev ...v1 URL until the server code can be repaired.


Sometimes client devs will copy-n-paste observed URLs and hardcode them into their client apps. Which deprives server devs of the opportunity to simultaneously offer multiple bug-fix versions of an endpoint during a planned transition period.

To encourage clients to respect the contract, server developers might choose to incorporate a few bits of SHA3(secret_pepper + todays_date) in URL path prefixes.

2
  • 1
    The name of the link is considered part of the API (or should be). It's the indirection that's important, plus (don't forget!) the existence or non-existence of a particular named link constrains the operations permitted on that object, which is therefore under control of the server.
    – davidbak
    Oct 12, 2023 at 0:52
  • IIRC according to Fielding one should not use v1 or v2 in the URLs, but rather choose a different media-type, so there should never be a change in the URL structure just because of versions. You also say that that the job might "automagically does the Right Thing". The job has no magic. It only knows about those two relations and if there is a braking change in the format which in your example goes along with a new rel "toprint_v2" a developer must change the jobs code to implement that change anyway. So why is it better to change the used rel instead of just changing the used URI?
    – wertzui
    Oct 12, 2023 at 6:11
1

In the materials I have read, the two "wins" for HATEOAS are:

  • It's easier to make changes to the URL space.
  • It helps developers to discover the API.

In practice, I don't believe either of these points to be true.

It's likely real world clients of a HATEOAS API will hardcode URL relationships instead of actually following the links, so if you try to change your URL space you will break them anyway. Hence in all likelihood you will end up with a regular API versioning schema so that you can deprecate the old API over time.

You can only "discover" API paths by actually calling the endpoints. This may be possible for read-only endpoints but your probably don't want to be calling POST's and PUT's just to figure out what options you could do next. Additionally there is always a possibility that there is some link that is data dependent - only shows up given a specific input. In short I would take good API documentation over HATEOAS any day.

Given my dislike/view that HATEOAS is inherently flawed, I am temped to say that no, there are no other reasons to use HATEOAS in your scenario.

1

There is no particular reason to use HATEOAS in a fully automated job. Most likely it just complicates things. Even the human-navigated web has the concept of "bookmarks" where you save an URL, so you later can access it directly rather than having to navigate through hypertext. Why should it be more difficult for an automated job? Just hardcode the endpoints.

The issue of changing URL's is a red herring. URL's are not supposed to change except when the semantics change. If a service changes its endpoints it means it breaks the contract, so it might also have changed the semantics of the service calls. So you need to redesign the Job anyway in that case.

HATEOAS is part of the REST architectural style which is a description of how the World Wide Web works as a distributed hypermedia system navigated by humans. You download a page that contains some links, and based on the description of the links you decide which to follow, according to what you are trying to achieve. In other words, content and navigation are intermingled in the media formats. This was in contrast to older information system where content and navigation was cleanly separated, eg. into menus and documents.

But there is no reason to use this for an automated job as you describe, where the tasks to perform are pre-defined. Another architectural style, Remote Procedure Calls, might be more appropriate for an automated job.

Your Answer

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

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