Stephen Last
Stephen Last

Reputation: 5781

REST API sub resources, data to return?

If we have customers and orders, I'm looking for the correct RESTful way to get this data:

{
  "customer": {
    "id": 123,
    "name": "Jim Bloggs"
    "orders": [
      {
        "id": 123,
        "item": "Union Jack Keyring",
        "qty": 1
      }, {
        "id": 987,
        "item": "London Eye Ticket",
        "qty": 5
      }
    ]
  }
}
  1. GET /customers/123/orders
  2. GET /customers/123?inc-orders=1

Am I correct that the last part/folder of the URL, excluding query string params, should be the resource returned..?

If so, number 1 should only return order data and not include the customer data. While number 2 is pointing directly at customer 123 and uses query string params to effect/filter the customer data returned, in this case including the order data.

Which of these two calls is the correct RESTful call for the above JSON..? ...or is there a secret number 3 option..?

Upvotes: 4

Views: 3799

Answers (4)

user5547025
user5547025

Reputation:

The Resource (identified by the complete URL) is the same, a customer. Only the Representation is different, with or without embedded orders.

Use Content Negotiation to get different Representations for the same Resource.

Request

GET GET /customers/123/
Accept: application/vnd.acme.customer.short+json

Response

200 OK
Content-Type: application/vnd.acm.customer.short+json

{
  "customer": {
    "id": 123,
    "name": "Jim Bloggs"
  }
}

Request

GET GET /customers/123/
Accept: application/vnd.acme.customer.full+json

Response

200 OK
Content-Type: application/vnd.acme.customer.full+json

{
  "customer": {
    "id": 123,
    "name": "Jim Bloggs"
    "orders": [
      {
        "id": 123,
        "item": "Union Jack Keyring",
        "qty": 1
      }, {
        "id": 987,
        "item": "London Eye Ticket",
        "qty": 5
      }
    ]
  }
}

Upvotes: 1

Roman Vottner
Roman Vottner

Reputation: 12839

I'm looking for the correct RESTful way to get this data

Simply perform a HTTP GET request on a URI that points to a resource that produces this data!

TL;DR

  • REST does not care about URI design - but on its constraints!
  • Clients perform state transitions through possible actions returned by the server through dynamically identified hyperlinks contained within the response.
  • Clients and servers can negotiate on a preferred hypermedia type
  • Instead of embedding the whole (sub-)resource consider only returning the link to that resource so a client can look it up if interested

First, REST does not really care about the URI design as long as the URI is unique. Sure, a simple URI design is easier to understand for humans, though if compared to HTML the actual link can be hidden behind a more meaninful text and is thus also not that important for humans also as long as they are able to find the link and can perform an action against it. Next, why do you think your "response" or API is RESTful? To call an API RESTful, the API should respect a couple of constraints. Among these constraints is one quite buzzword-famous: hypertext as the engine of application state (HATEOAS).

REST is a generalized concept of the Web we use every day. A quite common task for a web-session is that a client requests something where the server sends a HTML document with plenty of links and other resources the client can use to request further pages or stream a video (or what ever). A user operationg on a client can use the returned information to proceed further, request new pages, send information to the server etc, etc. The same holds true for RESTful applications. This is was REST simply defines as HATEOAS. If you now have a look at your "response" and double check with the HATEOAS constraint you might see that your response does not contain any links to start with. A client therefore needs domain knowledge to proceed further.

JSON itself isn't the best hypermedia type IMO as it only defines the overall syntax of the data but does not carry any semantics, similar to plain XML which though may have some DTD or schemas a client may use to validate the document and check if further semantic rules are available elsewhere. There are a couple of hypermedia types that build up on JSON that are probably better suited like f.e. application/hal+json (A good comparison of JSON based hypermedia types can be found in this blog post). You are of course entitled to define your own hypermedia type, though certain clients may not be able to understand it out of the box.

If you take f.e. a look at HAL you see that it defines an _embedded element where you can put in certain sub-resources. This seems to be ideal in your case. Depending on your design, orders could also be a resource on its own and thus be reachable via GET /orders/{orderId} itself. Instead of embedding the whole sub-resource, you can also just include the link to that (sub)resource so a client can look up the data if interested.

If there are cases where you want to return only customer data and other cases where you want also to include oder data you can f.e. define different hypermedia types (based on HAL f.e.) for both, one returning just the customer data while the other also includes the oder data. These types could be named like this: application/vnd.yourcompanyname.version.customers.hal+json or application/vnd.yourcompanyname.version.customer_orders.hal+json. While this is for sure an development overhead compared to adding a simple query-parameter to the request, the semantics are more clear and the documentation overhead is on the hypermedia type (or representation) rather then the HTTP operation.

You can of course also define some kind of view structure where one view only returns the customer data as is while a different view returns the customer data including the orders similar to a response I gave on a not so unrelated topic.

Upvotes: 0

arjabbar
arjabbar

Reputation: 6404

You have 3 options which I think could be considered RESTful.

1) GET /customers/12 But always include the orders. Do you have a situation in which the client would not want to use the orders? Or can the orders array get really big? If so you might want another option.

2) GET /customers/123, which could include a link to their orders like so:

{
  "customer": {
    "id": 123,
    "name": "Jim Bloggs"
    "orders": {
       "href": "<link to you orders go here>"
    }
  }
}

With this your client would have to make 2 requests to get a customer and their orders. Good thing about this way though is that you can easily implement clean paging and filtering on orders.

3) GET /customers/123?fields=orders This is similar to your second approach. This will allow clients to use your API more efficiently, but I wouldn't go this route unless you really need to limit the fields that are coming back from your server. Otherwise it will add unnecessary complexity to your API which you will have to maintain.

Upvotes: 3

Alex Marculescu
Alex Marculescu

Reputation: 5770

The JSON that you posted looks like what would be the result of

GET /customers/123

provided the Customer resource contains a collection of Orders as a property; alternatively you could either embed them, or provide a link to them.

The latter would result in something like this:

GET /customers/123/orders

which would return something like

{
    "orders": [
      {
        "id": 123,
        "item": "Union Jack Keyring",
        "qty": 1
      }, 
      {
        "id": 987,
        "item": "London Eye Ticket",
        "qty": 5
      }
    ]
}

Upvotes: 0

Related Questions