Search code examples
restapijoinrelationshipmany-to-one

RESTful API with relationships


Lets assume I'm building an API for a restaurant and I have the following resources:

Donut(has_chocolate,has_sprinkles)

and

Receipt(cost,donut_id)

My Web App will want to display a table of receipts for the manager to view. Unfortunately the receipt object by itself isn't useful enough, the manager needs to see whether the donut has chocolate or not.

How best do I do this - I can think of 3 implementations:

1) Do a JOIN and return the receipt resource with the has_chocolate additional field

2) Do a JOIN and return the receipt resource with a donut object containing ALL relevant donut information

3) Pull in a page of receipt objects, collect and de-duplicate the donut_ids, and use them to pull in the required donut objects - either one at a time /donut/id, or all at once /donuts?ids=id1,id2,id3


Solution

  • A RESTful API should have endpoints that resolve to a resource or a collection of resources. You can then do an HTTP verb on that endpoint, in this case a GET. Therefore you need to make a decision about how do you define your resources for the API user?

    Are there two resources, donuts and receipts, like in your SQL? Do you want to define a donut as a resource that has a receipt as one of the fields?

    Either is fine, the problem is when you start making routes that are a resource 'with something extra on top'. That starts to become hard for the consumer to understand and not RESTful.

    If it were me I would choose option 3.

    • Define two separate endpoints for collections of donuts and receipts: /v0/donuts/ and /v0/receipts/
    • Allow the consumer to join on the client side by exposing the neccessary filters /v0/donuts/?ids=1,2,3,8

    You could make a /v0/events/< id>/ route but seeing as the use-case is to always get a batch of donuts then this would result in too many round-trips for the consumer.

    It also sounds like you want to paginate these collections. In this case you should define a max_page_size, default_page_size and you should return your client a next_page field in the response (which is null if it's the last page). You would also have to decide what to paginate on, probably the id in this case.