Nobody in the REST community says REST is easy. HATEOAS is just one of the aspects that adds difficulty to a REST architecture.
People don't do HATEOAS for all the reasons you suggest: it's difficult. It adds complexity to both the server-side and the client (if you actually want to benefit from it).
HOWEVER, billions of people experience the benefits of REST today. Do you know what the "checkout" URL is at Amazon? I don't. Yet, I can checkout every day. Has that URL changed? I don't know it, I don't care.
Do you know who does care? Anyone who's written a screen-scraped Amazon automated client. Someone who has likely painstakingly sniffed web traffic, read HTML pages, etc. to find what links to call when and with what payloads.
And as soon as Amazon changed their internal processes and URL structure, those hard-coded clients failed -- because the links broke.
Yet, the casual web surfers were able to shop all day long with hardly a hitch.
That's REST in action, it's just augmented by the human being that is able to interpret and intuit the text-based interface, recognize a small graphic with a shopping cart, and suss out what that actually means.
Most folks writing software don't do that. Most folks writing automated clients don't care. Most folks find it easier to fix their clients when they break than engineer the application to not break in the first place. Most folks simply don't have enough clients where it matters.
If you're writing an internal API to communicate between two systems with expert tech support and IT on both sides of the traffic, who are able to communicate changes quickly, reliably, and with a schedule of change, then REST buys you nothing. You don't need it, your app isn't big enough, and it's not long-lived enough to matter.
Large sites with large user bases do have this problem. They can't just ask folks to change their client code on a whim when interacting with their systems. The server's development schedule is not the same as the client development schedule. Abrupt changes to the API are simply unacceptable to everyone involved, as it disrupts traffic and operations on both sides.
So, an operation like that would very likely benefit from HATEOAS, as it's easier to version, easier for older clients to migrate, easier to be backward compatible than not.
A client that delegates much of its workflow to the server and acts upon the results is much more robust to server changes than a client that does not.
But most folks don't need that flexibility. They're writing server code for 2 or 3 departments, it's all internal use. If it breaks, they fix it, and they've factored that into their normal operations.
Flexibility, whether from REST or anything else, breeds complexity. If you want it simple, and fast, then you don't make it flexible, you "just do it", and be done. As you add abstractions and dereferencing to systems, then stuff gets more difficult, more boilerplate, more code to test.
Much of REST fails the "you're not going to need it" bullet point. Until, of course, you do.
If you need it, then use it, and use it as it's laid out. REST is not shoving stuff back and forth over HTTP. It never has been, it's a much higher level than that.
But when you do need REST, and you do use REST, then HATEOAS is a necessity. It's part of the package and a key to what makes it work at all.
Example:- To understand it better let’s look at the below response of retrieve user with id 123 from the server (http://localhost:8080/user/123
):
{
"name": "John Doe",
"links": [{
"rel": "self",
"href": "http://localhost:8080/user/123"
},
{
"rel": "posts",
"href": "http://localhost:8080/user/123/post"
},
{
"rel": "address",
"href": "http://localhost:8080/user/123/address"
}
]
}