Search code examples
resturl-routingbackend

REST API Design superfluous ids


Was arguing with someone about REST API Design.

I have the routes GET /customers and GET /customers/<c-id> where c-id is the id of a customer.

Now the question is about adding a route for projects.

GET /customers/<c-id>/projects gives all projects of a customer. That someone suggested now to add GET /customers/<c-id>/projects/<p-id> to get information about one specific project. I have a bad feeling about that. Since p-id is a unique id for all projects, the c-id is not needed in the request at all. GET /projects/<p-id> should just do. c-id also is an attribute of the returned project json (every project belongs to just one customer). So this is the main question here: is it consistent with good practice to have the in my opinion superfluous c-id in the request?

One argument from the other side was this example from a blog:

GET /cars/711/drivers/4

But as far a I understand this only applies as good practice if 4 is not a unique identifier, but rather an enumeration in the example (the fourth driver of this car).

Further information:

Actually GET /projects is not used at the moment. So I also thought about adding it and using GET /projects?customerid=<c-id> instead of GET /customers/<c-id>/projects. What do you think about that?

Also the application in question has a permissions layer. So someone who's not allowed to access a specific customer should not be allowed to access projects of this customer, even if he knows the project-id. It was argued that this restriction is better expressed in the GET /customers/<c-id>/projects/<p-id> route. (The permission is actually decided upon a user-id attribute which is a property of customer and project likewise.) Would this change your answer to the question?


Solution

  • So this is the main question here: is it consistent with good practice to have the in my opinion superfluous c-id in the request?

    Sure, why not?

    REST doesn't care what spellings you use for URI.

    Where I think you are getting tangled up; there is no rule that says each entity in your domain model must have one and only one resource associated with it.

    It is perfectly acceptable to have resources like

    /customers/<c-id>/projects/<p-id>
    /customers/<c-id>/projects/<local-index>
    /projects/<p-id>
    /9e7b964a-c87a-4184-84b1-24132aabab66
    

    that all map to the same concept in your domain model, and therefore return the same representations, or redirect to each other, or whatever.

    It was argued that this restriction is better expressed in the GET /customers//projects/ route. (The permission is actually decided upon a user-id attribute which is a property of customer and project likewise.) Would this change your answer to the question?

    No, because identification and security are orthogonal concerns.

    Actually GET /projects is not used at the moment. So I also thought about adding it and using GET /projects?customerid= instead of GET /customers//projects. What do you think about that?

    Same as before

    /customers/<c-id>/projects
    /projects?customerid=<c-id>
    /a685ee45-f366-462b-a47a-dff61f98dd1e
    

    ... are all perfectly reasonable choices.

    One thing which you may want to consider is RFC 3986, which specifies the rules for computing a new identifier given a base and a relative reference. Dot segments can be a convenient shorthand for directing the client to another reference without needing to worry about which base identifier is currently in scope.