Contents

Returning Lists Of Items In JSON APIs

Contents

It happens often that we need to implement an endpoint that returns a list of items with a “canonical” id. A typical example is a /users endpoint that returns a list of users.

In this post, I will advocate for returning a list of objects each of which contains an id key and not a single object where ids are associated with their canonical values.

// Good
[
    {id: 1, name: 'Emelia', age: 12},
    {id: 2, name: 'Cassidy', age: 96}
]
// Bad
{
    1: {name: 'Emelia', age: 12},
    2: {name: 'Cassidy', age: 96}
}
Communicate Intent

The first reason to return a list of items is that, semantically, it is what the endpoint returns. In API designing, communicating intent is of paramount importance. We should make sure intent is well communicated before considering any other API feature such as performance.

Self-Contained Items

When you return a list of items, you make sure that each item is self-contained. Looking at the example above, we can see that the items are different,
{id: 2, name: 'Cassidy', age: 96} is self-contained whereas {name: 'Cassidy', age: 96} requires context to be interpretted correctly.

And here, there’s a slight subtility. In {id: 2, name: 'Cassidy', age: 96}, the id is a number. In {2: {name: 'Cassidy', age: 96}}, the id is a string. In javascript, ({2: {name: 'Cassidy', age: 96}})[2] will return the expected item, but other programming languages such as python will not. JSON supports very few types, we should make sure we leverage them where we can.

Wrap Responses Into Larger Objects

A nice consequence of returning a list of items it’s that it forces the endpoint to wrap the return value inside a larger object. To improve the change of the endpoint to be backward compatible, we should always wrap an API response into a larger object. In our case, a good API response would be.

{
    users: [
        {id: 1, name: 'Emelia', age: 12},
        {id: 2, name: 'Cassidy', age: 96}
    ]
}

In this situation, if down the line a product requirement about metadata shows up, we’ll have a good chance to be able to support it. For instance, if we need to add support for pagination, we could always add it.

{
    users: [
        {id: 1, name: 'Emelia', age: 12},
        {id: 2, name: 'Cassidy', age: 96}
    ],
    page: 23,
    totalNumberOfPage: 23,
    numberOfItemPerPage: 10
}
Appeal To Authority

If these arguments are not convincing enough, perhaps looking at popular public API such as gitlab, github, slack, or even api-football is more convincing. All these endpoints return lists of objects wrapped into larger objects. It is important to state that these examples are not cherry-picked; public APIs are generally returning lists of items wrapped into larger objects.