skip to content

You are setting the response format for a new family of internal and partner-facing REST services. How would you choose between plain JSON, HAL, JSON:API and Siren, and why do those formats register distinct media types such as application/hal+json instead of just using application/json?

level: principalimportance: nice to knowfreq 24%

answer

  1. choose from consumers and their release cadence
  2. plain JSON / HAL / JSON:API / Siren ladder
  3. media type = processing contract, enables Accept negotiation
  4. +json suffix = parseable by generic tooling; vnd. and prs. trees
  5. prove it with one real link-following client

basics

~20 s

Choose by client population: plain JSON for lockstep first-party clients, HAL for light linking, JSON:API for graph-shaped entity APIs needing shared conventions, Siren or HAL-FORMS for generic clients that must be told how to act. Distinct media types make the format negotiable, and the +json suffix marks it as JSON-parseable.

solid answer

~60 s

I start from who consumes it. Generated SDKs released with the API get little from hypermedia - plain JSON with a documented links convention is honest. Long-lived or third-party clients over a real state machine benefit from HAL's links. A graph of entities with many clients inventing their own include and paging syntax argues for JSON:API. A generic console or workflow driver needs action descriptions, so Siren or HAL-FORMS. The media type is the processing contract, not decoration. Because application/hal+json is distinct from application/json, a client can ask for what it can parse via Accept, and one endpoint can serve a plain representation to a simple consumer and a hypermedia one to a smart one. The +json structured syntax suffix tells generic tooling the payload parses as JSON even when the semantics are unknown; vnd. marks a vendor-tree type, prs. a personal one. What I would not do is pick a heavy format because it looks rigorous. The lock-in is real: serializers, test fixtures, partner contracts and client libraries all follow the choice.

code

http · 13 lines
http
GET /orders/42 HTTP/1.1
Accept: application/hal+json, application/json;q=0.8

HTTP/1.1 200 OK
Content-Type: application/hal+json
Vary: Accept

GET /orders/42 HTTP/1.1
Accept: application/json

HTTP/1.1 200 OK
Content-Type: application/json
Vary: Accept

go deeper

for a junior

Know that these formats exist, that each has its own media type, and that Accept lets a client ask for the one it understands.

for a middle

Compare the formats on what they add - links, graph conventions, actions - and explain negotiation and the +json suffix.

for a senior

Argue the choice from consumer needs and implementation cost, and cover Vary, correct Content-Type, and migration difficulty.

for a principal

Own it as a standard: consumer analysis, lightest format that serves the hardest consumer, proof by a real client, negotiated media types, and one shared serialization stack across services.

## Decide from the consumers, not from purity The question every format debate should start with is: who is on the other end, and can I redeploy them? - **One first-party frontend, released with the API.** Hypermedia indirection buys little. Plain JSON, an OpenAPI document, and a documented convention for pagination links is coherent and cheap. - **Many clients, some third-party, long-lived.** Links pay: URL structure stops being a contract, and server-computed affordances mean policy changes reach clients you cannot rebuild. HAL is the minimal step. - **Graph-shaped entity domain, many teams.** JSON:API's value is less hypermedia than standardisation - one answer for includes, sparse fieldsets, sorting, paging and error shape, plus deduplication and off-the-shelf client stores. - **Generic consumers - admin consoles, workflow engines, agents.** They need to be told how to act, not just where to go: Siren, or HAL-FORMS on top of HAL. ## Why the media type is not cosmetic A media type names the rules for interpreting the bytes. application/json says this is JSON and nothing more; it does not tell a consumer that _links is reserved, or that data holds typed resources. Declaring application/hal+json or application/vnd.api+json means: 1. **Negotiation.** A client sends Accept with what it understands, and one resource can serve several representations - plain JSON to a simple integration, HAL to a hypermedia-aware one - without separate URLs. 2. **Correct failure.** A client that cannot handle the format finds out from the type, rather than by misparsing a document whose reserved members it ignores. 3. **Evolution.** The format's rules can change under its own identifier without breaking consumers of another. The **+json structured syntax suffix** matters for generic tooling: proxies, loggers and schema tools that know nothing about HAL can still see that the payload is JSON and parse or pretty-print it. Registration trees matter too: names under **vnd.** belong to a vendor, **prs.** to a personal or experimental format, and the standards tree requires registration with IANA. A lighter alternative to minting a type is the profile parameter, which flags a specific set of semantics layered on a base type. JSON:API is a useful cautionary example of how strict this can get: it defines precise behaviour around media type parameters, requiring 415 or 406 responses in specified cases - so adopting it means adopting its negotiation rules too. ## What the choice locks in - **Serialization stack.** JSON:API and Siren need real machinery. In the Spring world, Spring HATEOAS supplies RepresentationModel, EntityModel, CollectionModel and link builders that derive hrefs from controller routes, and configuration to emit HAL, HAL-FORMS or Siren - convenient, but now a dependency in the middle of your response layer. - **Tests and fixtures.** Every fixture is written in the format; migrating later touches every test. - **Partner contracts.** External consumers make the format effectively permanent. - **Team fluency.** A format nobody understands is worked around, and half-obeyed conventions are worse than none. ## How I would actually decide Write down the consumers and their release cadence. Pick the lightest format that serves the hardest consumer. Prove it with one real client that genuinely follows links or renders actions - if no such client exists or is planned, the format's benefits are theoretical and you are buying payload and complexity. Keep the option open by negotiating on media type rather than hardcoding one, and standardise the decision at platform level so consumers do not face four dialects across five services.

  • What does the +json suffix in application/vnd.api+json actually tell a consumer?
    That the payload uses JSON as its underlying syntax, so any generic tool can parse, log or pretty-print it even without knowing the format's semantics. The part before the suffix identifies those semantics - here the vendor-tree JSON:API type. Without the suffix, generic tooling would have to guess or special-case the name.
  • How do you avoid a platform ending up with four different hypermedia dialects?
    Make the format a platform-level standard rather than a per-service choice, with one shared serialization setup and shared test helpers so the default path is also the easy path. Allow deviations only with a written reason, and negotiate on media type so that supporting an additional representation is an explicit, reviewable addition rather than a silent divergence.

saying these in an interview costs you the question

  • Adopting a heavy format with no client that follows links or renders actions
  • Serving HAL or JSON:API documents under Content-Type application/json
  • Treating the format choice as reversible after external partners integrate
  • Believing a media type suffix such as +json changes the document's semantics rather than its syntax

context