0

In designing a RESTful API, the following call gives us basic information on user 123 (first name, last name, etc):

/api/users/123

We have a lot of information on users so we make additional calls to get subresources on a user like their cart:

/api/users/123/cart

For an admin page we would like to see all the cart information for all the users. A big table listing each user and some details about their cart. Obviously we don't want to make a separate API call for each user (tons of requests). How would this be done using RESTful API patterns?

/api/carts/users came to mind but then you'd in theory have 2 ways to get a specific user's cart by going /api/carts/users/123.

Jeremy
  • 1,023
  • 3
  • 18
  • 33
  • 1
    Can u define two apis, one for users like `/api/users/{userId}` and another for carts like`/api/carts/{cartId}`? – Sher10ck Jul 17 '15 at 21:10
  • A cart object contains an attribute userId. For admin page, you can use something like '/api/carts?page=1' to get first X carts. Then use userId (extract from cart objects) to send another request like 'api/users?id = id1, id2, id3...` to get user information. – Sher10ck Jul 17 '15 at 21:22
  • Cart is a bit misleading. It's not so much an entity in my case. It's more of a summary of stats and analytics about the user's shopping behavior. So it doesn't really have an ID, but is just a bunch of calculated stats and summary information created in a service method of the API. I call /api/users/123/cart to get a user's stats about their shopping. I can get a "cart" (stats/summary info) about a business as well by calling /api/org/111/cart. So if I want a list of carts (or stats) for all users, I'm wondering if /api/carts/users is the right way. Seems somewhat wrong, but not sure. – Jeremy Jul 18 '15 at 03:33

2 Answers2

1

This is generally solved by adding a deref capability to your REST server. Assuming the response from your user looks like:

{
  ...
  cartId: "12345",
  ...
}

you could add a simple dereference by passing in the query string "&deref=cart" (or however you setup your syntax.)

This still leaves the problem of making a request per user. I'd posit there are two ways to generally do this. The first would be with a multiget type of resource (see [1] for an example). The problem with this approach is you must know all of the IDs and handle paging yourself. The second (which I believe is better) is to implement an index endpoint to your user resource. Indexing allows you to query a resource (generally via a query string such as firstName=X or whatever else you want to sort on.) Then you should implement basic paging so you're not passing around massive amounts of data. There are tons of examples of paging, but the simplest would be to specify a number (count=20) a start token (since=X) and a sort order (sort=-createdAt).

These implementations allow you to then ask for all users and their carts by iterating on the index endpoint. You might find this helpful as a starting point for paging [2].

[1] - How to construct a REST API that takes an array of id's for the resources

[2] - Pagination in a REST web application

Community
  • 1
  • 1
Ned Rockson
  • 1,095
  • 7
  • 16
  • Cart is not so much an entity in my case. It's more of a summary of stats and analytics about the user's shopping behavior. So it doesn't really have an ID, but is just a bunch of calculated stats and summary information created in a service method of the API. I do agree with you about using indexing/query params to do the pagination. So are you suggesting going with a URL route like /api/carts/users?count=20&first=0 ? – Jeremy Jul 18 '15 at 03:29
  • 1
    @Jeremy It's hard to say. Entities that are calculated via an endpoint such as this make things a little harder to do. If you think about what you're doing logically, you're fetching a bunch of users & their cart information. Your suggestion is fetching carts and potentially indexing users on those carts. Sounds confusing. I think that user is the object here and even if cart is calculated, treating it as a field on the object (e.g. accessible via query parameters) is worth considering for clarity. – Ned Rockson Jul 20 '15 at 19:54
0

For some reason I was under the assumption that having 2 URIs to the same resource was a bad thing. In my situation /api/users/123/cart and /api/carts/users/123 would return the same data. Through more research I've learned from countless sources that it's acceptable to have multiple URIs to the same resource if it makes sense to the end user.

In my case I probably wont expose /api/carts/users/123, but I'm planning on using /api/carts/users with some query parameters to return a subset of carts in the system. Similarly, I'm going to have /api/carts/orgs to search org carts.

A really good site I found with examples and clear explanations was the REST API Tutorial. I hope this helps others with planning their API URIs.

Jeremy
  • 1,023
  • 3
  • 18
  • 33