REST-full design: recommended approach for fetching related entities

Architecturerest

Suppose I have two entities author and post, were every post has an author.
Implemented are these REST calls

  1. Get a paginated list of posts: GET /posts?page={page}

  2. Get a list of authors: GET /authors

  3. Get a single author: GET /authors/{id}

Suppose I also have a client using the REST service. This client wants to show a paginated list of posts with related authors.

Now how would the REST service work regarding the related entities? There are the following methods:

  1. Let the GET /posts?page=1 call return the posts and the related authors in a single call. Optionally make this a separate parameter, f.e. GET /posts?page=1&related=true or GET /posts/related?page=1.

  2. Let the client loop through the posts and get the author for every post using the GET /author/{id} call.

  3. Let the REST call for the authors accept a list of ID's as a filter, so the client can get the related authors in one call, f.e. GET /authors?ids=10,12,16,30,43,44,58.

Method 1 is the easiest one from the client's perspective since it gets all the necessary data in one call. But the REST service is limited here, it shouldn't need to fit any particular client because every client has other requirements.

Method 2 is the cleanest REST approach, but let's say that a page has 30 posts, the client has to make 30 additional REST calls to retrieve the related authors. This is very inefficient.

Method 3 is perhaps better of the three, since it is a relatively clean REST implementation (compared to method 1) and it requires only one extra call per page.

Is there a recommended approach here?

Best Answer

That's, what links are for:

E.g. requesting the first post via GET http://example.com/posts/1

{
    "id": "1",
    "author": {
        "nickname": "me"
    },
    "links": [
        {
            "rel": "self",
            "href": "http://example.com/posts/1"
        },
        {
            "rel": "author",
            "href": "http://example.com/authors/me"
        }
    ]
}

The response contains a link to the author (named me). If you want to retrieve all of me's posts, you would do a GET to http://example.com/posts?author=me; and if you want it paginated, you would add a queryparameter like http://example.com/posts?author=me&page=1.

Say, you have collaborations between various authors, the initial answer would've been:

{
    "id": "1",
    "authors": [
        {
            "nickname": "me"
        },
        {
            "nickname": "mo"
        },
        {
            "nickname": "ma"
        }
    ],
    "links": [
        {
            "rel": "self",
            "href": "http://example.com/posts/1"
        },
        {
            "rel": "author",
            "href": "http://example.com/authors/me"
        },
        {
            "rel": "author",
            "href": "http://example.com/authors/mo"
        },
        {
            "rel": "author",
            "href": "http://example.com/authors/ma"
        }
    ]
}

On the other hand, under the links-section of the author, there should only be a reference to the latest post (to prevent too much data in there).

If you want to get a set of all collaborative posts from the trio above, you would query as follows:

GET http://example.com/posts?author=mo,me,ma

If you want all posts - including the collaborative - from the trio, you would query like this:

GET http://example.com/posts?author=mo|me|ma

This is a clean and straightforward approach.

To express, that something is related, you use links. If you want to filter you do it via querystring.