Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

REST design for "related" resources

Suppose I have a customer that has multiple accounts. Really any data object that has a "1 to many" relationship with another complex object will do.

An example might be:

{ id: 1, name: "Bob", accounts: [ { id: 2, name: "Work account" }, { id: 3, name: "Home account" } }

My question is, when is it appropriate/better to expose the accounts as a sub-resource of the customer, vs. as a separate resource? Or both?

For example, my first intuition would be to have: /customers/1 return the object above. If you wanted to modify one of the accounts, you'd have to POST to /accounts/2.

The other way to go about it (I have seen in some APIs) is to expose another route /customers/1/accounts which would return the array above, and then set up POST/PATCH routes there to allow API users to mutate the array of accounts.

My problem with that approach is that if the array of accounts are actually "included by reference", it's not really clear whether that REST route is modifying the account or if it's merely modifying the linkage between customer and the account.

Is there a best practice here?

like image 687
FrobberOfBits Avatar asked Mar 28 '16 21:03

FrobberOfBits


People also ask

How do you represent resources in REST?

REST uses various representations to represent a resource where Text, JSON, XML. The most popular representations of resources are XML and JSON.

What are REST resources?

Resources are the basic building block of a RESTful service. Examples of a resource from an online book store application include a book, an order from a store, and a collection of users. Resources are addressable by URLs and HTTP methods can perform operations on resources.

What is REST design?

What is REST? In 2000, Roy Fielding proposed Representational State Transfer (REST) as an architectural approach to designing web services. REST is an architectural style for building distributed systems based on hypermedia. REST is independent of any underlying protocol and is not necessarily tied to HTTP.


1 Answers

This is a good question and is up for discussion (there isn't a "correct" answer). Here are some points you may want to consider:

  1. Having the child account resource embedded in the customer resource will cause more data to always be sent back with the /customers/{id} request.

  2. Having the child account resource non-embedded will require a client to send multiple HTTP requests if it needs both basic customer information and also account information.

  3. You'll want to determine exactly how your security paradigm will work with embedded resources. (i.e. Is it possible to be allowed to get the information of a customer but not be allowed to see the customers accounts?)

  4. Does it ever make sense to have an account without a customer in your domain? Can accounts transfer ownership? If not, then /customers/{id}/accounts/{acct_id} makes more sense.

Implied in the definition of REST, issuing HTTP methods on a URI is modifying a resource identified by the URI, so by default, you're always modifying the account and not the linkage between the customer and account.

If you needed functionality to modify the linkage of accounts, you could invent a resource like, "account link request" (POST /accounts/{id}/linkreqeust or something of that nature). This request could have a state in its own right, where you would have back-end functionality that would evaluate the request and then determine if an account should be linked or detached to/from a customer and then do the attach/detach process.

In summary, there's nothing in REST that prevents you from referencing the same resource with different links (/accounts/{id}; /customers/{id}/accounts/{acct_id}). My preference is if there are no security implications to having the sub-resource, then have it in conjunction with an endpoint to access the sub-resource by itself. If accounts could be tied to multiple users (or have no customers), I would also expose the /accounts/{id} endpoint.

ex. /customers/1:

{
  "id": "1",
  "name": "John Doe",
  "accounts": {
    "collection": [
      {
        "id": "1",
        "type": "savings",
        "balance": "$400",
        "links": {
          "self": "/customers/1/accounts/1"
        }
      },
      {
        "id": "2",
        "type": "checking",
        "balance": "$600",
        "links": {
          "self": "/customers/1/accounts/2",
          "detach" : "/customers/1/accounts/2/linkrequest?action=detach"
        }
      }
    ],
    "links": {
      "self": "/customers/1/accounts",
      "attach": "customers/1/accounts/linkrequest?action=attach"
    }
  },
  "links": {
    "self": "/customers/1"
  }
}
like image 174
Mackers Avatar answered Oct 14 '22 14:10

Mackers