Search code examples
restweb-applicationsapi-design

REST API design- handling foreign key references and different representations of the same resource


When creating API's, I'm often unsure as to the treatment of referenced values in my database as well as different representations of the same resource.

Let's say I have an order table with, among other, these fields: id, product_id(fk) status_id(fk).

Now, a GET request is made to this order:

GET /api/order/{order_id}. 

Two questions now arise, out of my inexperience:

1. What to return for the fields referencing either another resource (product) or a lookup table (status)?

The options I am considering are:

a) Provide only the fk id, and leave it to the client to then request the corresponding resource.

GET /api/order/1
{
  id: 1,
  status_id: 1,
}

b) Provide both the id, as well as the value that is likely relevant in the response. E.g. the response would include:

GET /api/order/1
{
  id: 1,
  status_id: 1,
  status_title: 'pending,
}

c) Do two queries in the back end, and nest them in the response:

GET /api/order/1
{
  id: 1,
  status: {
   id: 1,
   title: 'pending',
  },
}

2. How do you handle requests to this resource from different stakeholders?

E.g. surely a customer GETting an order shouldn't have access (nor probably wants it) to the same representation of that resource as a store administrator using his interface to fulfill the order.

Let's say, theoretical, the order table contains a field commission_rate stating the relevant commission internally negotiated on that order.

How do you now go about handling the aforementioned GET to indicate that you are trying to retrieve it from a certain perspective?

Perhaps

GET /api/order/{id}/customer

or

GET /api/order/{1}?view=customer


Solution

  • APIs tend to work at the domain level:

    Order {
      id: "123",
      customerId: "456"
      ...
    

    }

    databases work at the implementation level:

    rows with FK etc. No client ever needs to know what FK means or even see one.

    So it would be an idea to start designing the API from a domain perspective and then grouping endpoints into authorisation contexts, e.g. only sales reps can request commission_rate for an order.

    The domain objects themselves are "first class citizens", Order, Product, Sales etc and the authorisation context determines who/what can request each domain object (REST resource).

    e.g. a Customer may want to know when their Order will be Delivered while a SalesRep may want to know when the Order for that Customer was delivered so they can get their CommissionRate.

    GET /order/{id} -> Order accessed by (Custmer, SalesRep, Admin)
    GET /order/{id}/status -> OrderStatus accessed by (Custmer, SalesRep, Admin)
    GET /order/{id}/commission -> CommissionRate accessed by (SalesRep, Admin)
    

    in each call, the client wants to know a specific aspect of an Order. In terms of including other content, they may not want it but you could include relevant links as HATEOS extensions in the response, giving relevant API endpoints related to that Order such as the Product ordered, or even the API endpoint to get the Status etc.