Implementing HATEOAS

1. Understanding Hypermedia as Engine of Application State

HATEOAS: clients navigate by following links discovered in responses, not hardcoded URIs. Highest level (3) of Richardson Maturity Model.

AspectHATEOASWithout
URI knowledgeDiscovered from responsesHardcoded in client
State transitionsServer tells client what's possibleClient guesses based on state
CouplingLooseTight
{
  "id": 100,
  "status": "pending",
  "links": [
    {"rel": "self",   "href": "/orders/100", "method": "GET"},
    {"rel": "cancel", "href": "/orders/100/cancel", "method": "POST"},
    {"rel": "pay",    "href": "/orders/100/payments", "method": "POST"}
  ]
}

3. Using HAL Format

Hypertext Application Language — minimal hypermedia format.

FieldPurpose
_linksObject of link relations
_embeddedEmbedded related resources
Content-Typeapplication/hal+json

Example: HAL Response

{
  "id": 100,
  "amount": 250.00,
  "_links": {
    "self":  {"href": "/orders/100"},
    "user":  {"href": "/users/42"}
  },
  "_embedded": {
    "items": [
      {"sku": "ABC", "qty": 2, "_links": {"self": {"href": "/products/ABC"}}}
    ]
  }
}

4. Using JSON:API Specification

ElementPurpose
dataPrimary resource(s)
includedSideloaded relations
linksPagination, self
relationshipsResource linkage
Content-Typeapplication/vnd.api+json
relUse
selfCurrent resource URI
next / prevPagination
first / lastBoundaries
upParent resource
relatedRelated resource
StateAvailable Actions
order.status = pendingcancel, pay, edit
order.status = paidrefund, ship
order.status = shippedtrack, return
SourceExamples
IANA registryself, next, edit, collection
Custom (URI)https://api.example.com/rels/cancel
Compact (CURIE)ex:cancel with curies map

8. Implementing Resource Discovery

Example: API Root Document

{
  "_links": {
    "self":     {"href": "/"},
    "users":    {"href": "/users"},
    "orders":   {"href": "/orders"},
    "products": {"href": "/products"},
    "search":   {"href": "/search?q={query}", "templated": true}
  }
}
HeaderFormat (RFC 8288)
Single linkLink: </users/42>; rel="self"
MultipleComma-separated
With typeLink: <...>; rel="next"; type="application/json"

10. Balancing HATEOAS vs Simplicity

Use HATEOAS WhenSkip When
Public API, many clientsSingle internal client
State machines (workflows)Simple CRUD
Long evolution lifecycleRapid iteration
Discoverability mattersOpenAPI doc sufficient