Skip to content

Instantly share code, notes, and snippets.

@morten
Created March 6, 2012 21:22
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save morten/1989043 to your computer and use it in GitHub Desktop.
Save morten/1989043 to your computer and use it in GitHub Desktop.
Response structures for a JSON API

Exploring how to place navigational and other meta concerns in a JSON API.

A set of meta attributes

Mandates that there will always be a "values" key in the collection hash.

// This is a collection of boats
"boats": {
  "values": [ .. array of boat records .. ]
  "meta": {
    "self": "https://example.org/boats/page/1",
    "next": "https://example.org/boats/page/2",
    "prev": null,
    "size": 109
  }
}

// This is a boat record
"boat": {
  "meta": {
    "self": "https://example.org/boats/47",
    "sailors": "https://example.org/boats/47/sailors"
  }
  "id": 47,
  "name": "The Horse"
}

Present meta on the same level as the collection

This breaks the symmetry in how collections and records surface "meta attributes"

"boats": [ .. array of boat records .. ],
"meta": {
    "self": "https://example.org/boats?page=1",
    "next": "https://example.org/boats?page=2",
    "prev": null,
    "size": 109
  }
}

// This is a boat record
"boat": {
  "meta": {
    "self": "https://example.org/boats/47",
    "sailors": "https://example.org/boats/47/sailors"
  },
  "id": 47,
  "name": "The horse"
}

Move meta for the records as well

Adjust the boat record to align with how a collection sits next to the "meta" attributes. Navigating the response structure is becoming clunky at this point.

// This is a boat record
{
  "boat": {
    "id": 47,
    "name": "The horse"
  },
  "meta": {
    "self": "https://example.org/boats/47",
    "sailors": "https://example.org/boats/47/sailors"
  }
}

Reduce ambitions

Reduce ambitions and just give records a "url" attribute an no other meta information. Maybe there won't be a second rising of the semantic web after all.

// This is a collection of boats
"boats": [ .. array of boat records .. ]
"next": "https://example.org/boats?page=2",
"prev": null,
"size": 109

// This is a boat record
"boat": {
  "id": 47,
  "url": "https://example.org/boats/47",
  "name": "The Horse"
}

Use HTTP headers instead

Resources don't know about meta. Use HTTP headers.

Collections:

Link: <https://example.org/boats?page=2>; rel="next", <https://example.org/boats?page=1>; rel="self"
X-Collection-Size: 109

Records:

Link: <https://example.org/boats/47/sailors>; rel="sailors", <https://example.org/boats/47>; rel="self"
@dasch
Copy link

dasch commented Mar 6, 2012

If we can get away with it, I would greatly prefer to use headers. Adding complexity to our resource representations sucks.

Also: we should always add URLs directly in the representations of our resources, e.g. each object has a url attribute. We could also add URLs for resources, e.g. comments_url.

"boat": {
  "id": 123,
  "url": "http://im-on-a-boat.com/boats/123",
  "passengers_url": "http://im-on-a-boat.com/boats/123/passengers",
  "name": "The Horse"
}

@steveklabnik
Copy link

You might be interested in how some others are handling these kinds of questions: Collection+JSON, HAL+JSON, and this gist and this gist.

@steveklabnik
Copy link

The most important thing: Make sure this is defined in your media type somewhere, and serve the right type! As soon as you require these semantics, application/json doesn't cut it anymore!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment