Sirish Renukumar
Sirish Renukumar

Reputation: 1717

What is the correct design principle for HTTP GET responses in REST that need to return 2 different sets of responses

I wanted to understand which is the right approach to be followed when designing a HTTP GET response as per REST. I have the following requirement

class Employee {
   private long employeedID;
   private String name;
   private Date dob;
   private String address;
   private String department;
}

Modeling as per REST, the HTTP GET /employees would return the array of all employees. Similarly HTTP GET /employees/1 will return the employee with Id as 1

Now there is one UI driven workflow, where I need to display only the name and employeeID of each employee. Hence the existing response from the HTTP GET /employees is heavyweight (the other fields are unnecessarily transferred). Therefore I want to limit the response to only contain the name and employeeID of each employee.

I am evaluating the following options

Approach 1 :

Use the Content-Type HTTP header to indicate that the client making the HTTP GET /employees request needs a trimmed list of attributes in the response. I.e have some custom string in the Content-Type (viz application.summary+json) that will result in only the 2 attributes to be included in the response

Approach 2 :

Use an additional query parameter as the HTTP GET /employess?isSummary=true. In this case, on the server side , depending on the value of the isSummary parameter, I can return only the 2 attributes for each employee

Approach 3 :

Create a new REST endpoint itself that supports the trimmed down response i.e HTTP GET /employees/summaryDetails

In this case only the 2 attributes will be returned in the above endpoint.

Of these 3 approaches, which would follow most closely with REST ?

Thanks

Upvotes: 1

Views: 453

Answers (3)

Opal
Opal

Reputation: 84786

This answer is added as a follow-up to the discussion in comments below this answer.

Basically, yes I'm against HATEOAS. Why? The idea in general is good but:

  1. IMO it tends to remove the reasonable limits when it comes to number of endpoints. It seems that the answer of some developers to Will it be RESTful...? or How to do it in REST...? is very often: Add a new endpoint /data/{id}/add/ and it will be documented via _links meta field. This is not how it should be done. This way you can always add a new endpoint and appropriate link and finish with a huge number of endpoints that no one can understand or verify. E.g. this link returns the basic set of data:

    "http://foo.bar/employees/1"
    

    this returns further details:

    "http://foo.bar/employees/1/details"
    

    What if I need other subset of details? Will I add a new endpoint? And.. What if there are multiple different clients that need a mutually exclusive subsets of data? Each of these clients will have a dedicated endpoint? This is a nightmare!

  2. Links are not about URLs only but query params as well. Are they included in links? In what form? Templates? So I can't follow the link as is. Every param has a default value? I guess that is not simply possible to provide a default value for every query param.

  3. The mentioned discoverability and documentation. Trust me, for most of APIs you design, develop and deploy you'll need to write docs. Why? Because the company which ordered this API needs it. Does stripe.com follow HATEOS rules? No! So why it's so successful? Because it's extremely well documented and has libraries along with examples in multiple most popular languages and tools.

  4. Find time to view this talk, it's worth it.

And now after this short note about HATEOAS.

Approach #1

Headers should be used when the version of resource is changed (new fields are added or removed) itself rather than you need a particular fields or subset of a resource. So IMO this is not the way to go.

Approach #3

It's a totally bad idea for the reasons mentioned in the intro to this answer.

Approach #2

IMHO this is the way to go. As @leeor answered this is a popular, accepted and a flexible pattern.

You can extend it by adding a query param called e.g. view which is an enum (SIMPLE, EXTENDED, FULL) and represents a list of predefined views. This is the way to avoid adding new endpoints. Instead you add and document(!) new views. It's up to you if view and fields are mutually exclusive or the order they are processed.

Upvotes: 2

leeor
leeor

Reputation: 17781

I think something in the realm of Approach #2 is the way to go here. Fundamentally, you are still accessing the same search and result set in terms of the resource (Employee), and it is the same resource. so Approach #3 isn't really suitable.

That said, there are various ways to go about #2. One approach is to have a query string parameter representing the projection - kind of like a SQL projection. So something like:

GET /employees?fields=ID,name

I've worked with a few API's that work this way and they work quite well.

Upvotes: 3

sh0rug0ru
sh0rug0ru

Reputation: 1626

The problem with each of the listed approaches is that it complicates the API, and violates probably the most fundamental principle of REST, which is discoverability. The response gives no clue that any of the above API exists. You would have to read documentation (horror!). The fundamental rule of REST is HATEOAS: Hypertext As The Engine of Application State.

So, if you want an API that is the maximally RESTful, consider the following, which follows the standard known as HAL:

This:

HTTP GET /employees

yields:

[ {
    "employeeID": 1,
    "name": "Joe",
    "_links": [ {
        "rel": "self",
        "href": "http://foo.bar/employees/1"
    }, {
        "rel": "details",
        "href": "http://foo.bar/employees/1/details"
    } ]
}, {
    "employeeID": 2,
    "name": "Sam",
    "_links": [ {
        "rel": "self",
        "href": "http://foo.bar/employees/2"
    }, {
        "rel": "details",
        "href": "http://foo.bar/employees/2/details"
    } ]
} ]

Following a link:

HTTP GET /employees/1

yields:

{
    "employeeID": 1,
    "name": "Joe",
    "_links": [ {
        "rel": "self",
        "href": "http://foo.bar/employees/1"
    }, {
        "rel": "details",
        "href": "http://foo.bar/employees/1/details"
    } ]
}

And following another link:

HTTP GET /employees/1/details

yields:

{
    "employeeID": 1,
    "name": "Joe",
    "dob": "1985-04-23",
    "address": "123 Main St",
    "department": "Department of Redundant Links",
    "_links": [ {
        "rel": "self",
        "href": "http://foo.bar/employees/1"
    }, {
        "rel": "details",
        "href": "http://foo.bar/employees/1/details"
    } ]
}

For inspiration, check out the JIRA REST API, probably the best ones I've seen.

Upvotes: 0

Related Questions