Search code examples
restapi-design

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?


Solution

  • 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"
      }
    }