Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JSON Response Model design issue

I need help with designing the JSON response models of our API.

Let's say a getUsers call returns the following response model:

{
    "users":
    [
        {
            "userId": "1",
            "name": "Joe Soap",
            "companyId": "3"
        },
        {
            "userId": "2",
            "name": "Bob Jones",
            "companyId": "3"
        },
        {
            "userId": "3",
            "name": "Mary Jane",
            "companyId": "4"
        },
    ]
}

Now we have another call, let's say getUsersWithCompany. This includes company information. The question I have is:

Should I include this additional company data into each user or should it be a whole new companies list in the response model?

Solution 1: Combining user and company

{
    "usersWithCompany":
    [
         {
            "userId": "1",
            "name": "Joe Soap",
            "company":
            {
                "companyId": "3",
                "companyName": "Some Company Ltd",
                "contactNumber": "123123123"
            },
        },
         {
            "userId": "2",
            "name": "Mary Jane",
            "company":
            {
                "companyId": "4",
                "companyName": "Another Company Ltd",
                "contactNumber": "463463383"
            },
        }
    ]
}

Advantage: This might be easier when the model is consumed as the company data is available as the user list is being traversed.

Solution 2: Separate lists for user and company

{
    "users":
    [
        {
            "userId": "1",
            "name": "Joe Soap",
            "companyId": "3"
        },
        {
            "userId": "2",
            "name": "Bob Jones",
            "companyId": "3"
        },
        {
            "userId": "3",
            "name": "Mary Jane",
            "companyId": "4"
        },
    ],
    "companies":
    [
        {
            "companyId": "3",
            "companyName": "Some Company Ltd",
            "contactNumber": "123123123"
        },
        {
            "companyId": "4",
            "companyName": "Another Company Ltd",
            "contactNumber": "463463383"
        },
    ],
}

Advantage: This ensures that the user and company items are always consistent across calls. Less variety for the consumer.

We are using .NET Web Api 2.

like image 899
Dave New Avatar asked Mar 05 '14 08:03

Dave New


3 Answers

The design should take into account if the entities are weak or strong. (where weak means they don't live if the main entity is deleted). So for users and companies, I'd have a separate RESTFULL service like

/users
[
        {
            "userId": "1",
            "name": "Joe Soap",
            "companyId": "3"
        },
        {
            "userId": "2",
            "name": "Bob Jones",
            "companyId": "3"
        },
        {
            "userId": "3",
            "name": "Mary Jane",
            "companyId": "4"
        },
]

/companies
[
        {
            "companyId": "3",
            "companyName": "Some Company Ltd",
            "contactNumber": "123123123"
        },
        {
            "companyId": "4",
            "companyName": "Another Company Ltd",
            "contactNumber": "463463383"
        },
]

You also need to consider if you have some other reference to the companies catalog besides the users. If that is the case, I'll be even convinced about this approach.

like image 184
Tomas Fornara Avatar answered Sep 22 '22 03:09

Tomas Fornara


I would start out with the single list of combined company and user objects (pledging YAGNI "You ain't gonna needed it" wrt. to the double list). This is the most widely used solution I have seen - and it allows you to easily add more user-related information later on.

Later on, if it turns out that payload size becomes an issue (maybe because you simply have too many users in one response), you can add a new method/service that returns an optimized version of the result. But remember that zipping the output can get you a long way wrt. payload size - for free!

But "optimized version" can be different things: you suggested one solution (two lists), but maybe you later on discover that a better optimization would be to only return some kind of "incremental" or "delta" result wrt. a previous response.

It all depends on your use cases - and premature optimization makes you spent efforts were it may never be needed.

like image 34
Jørn Wildt Avatar answered Sep 19 '22 03:09

Jørn Wildt


I would suggest treating user and company as different resources. You can use a query parameter to specify whether or not to include the company information in the user payload.

GET /users

{
    "users":
    [
        {
            "userId": "1",
            "name": "Joe Soap",
            "company":
            {
                "self": "http:/my.server/companies/3"
            }
        },
        ...
    ]
}

GET /users?expand=company

{
    "users":
    [
        {
            "userId": "1",
            "name": "Joe Soap",
            "company":
            {
                "self": "http:/my.server/companies/3",
                "companyId": "3",
                "companyName": "Some Company Ltd",
                "contactNumber": "123123123"
            }
        },
        ...
    ]
}

This allows you to only get user information when you need it, to bundle company information when you need it, and to follow the URI from a specific user to their company without having to build the URI on the client. By keeping company as a separate resource, you also give yourself room to work with companies without needing a user as a reference point.

like image 37
Eric Stein Avatar answered Sep 19 '22 03:09

Eric Stein