5

In my current maintenance project, there is REST API resource URL like this:

/sites/<site id or site code>/buildings/<building id or building code>

In this endpoint URL, there are two parameters,

  • <site id or site code>
  • <building id or building code>

As the name indicates, these two parameters are ambiguous, say the value of the first parameter can be either site id or site code, the value of the second parameter can be either building id or building code. However, implicitly it means,

For instance, there is a building with 1 as building id and rake as building code, and it is located in the site with 5 as the site id and SF as the site code, then the following endpoint URL should retrieve the same result:

  • /sites/5/buildings/1
  • /sites/5/buildings/rake
  • /sites/SF/buildings/1
  • /sites/SF/buildings/rake

However, there is a hint in the HTTP header indicating the given value of the path parameter is code or ID. Even though, the implementation of such resource endpoint contains lots of if conditions due to the ambiguity. However, from the end-user's aspect, this seems to be handy

My question is whether such endpoint design is a good practice or a typical bad practice?

  • Why do you want to do that? – Thorbjørn Ravn Andersen Jun 26 at 21:33
  • I indeed would never want to do that. But the fact is the current existing project was done like this, and this project is a small part of a big product – Rui Jun 26 at 22:08
5

To add to what Ewan has said, since HTTP already has "stringly" typed parameters, so it can be impossible to parse the parameters correctly. You should aim for precision in being able to express your intent. You will already have a lot of other factors to deal with, but you don't want to have design ambiguity as part of it.

Ewan gives the counter-example of having both siteId: 1 and siteCode: "1". That is bad, since you will eventually run into that, and it can become more and more complex. Avoiding that will make your life easier. And even if you don't think you will have that problem, you must have logic to determine the intent of the client, but that is backwards-- the client should determine their intent and be able to express it unambiguously to you by selecting an endpoint.

A single client will almost always pick one endpoint, so removing as many edge cases from that endpoint as possible will simplify usage for your clients as well. A client would rarely want to pass a siteId and siteCode to the same endpoint.

Function overloading in normal code works because of the type encoding of parameters, when that type information does not exist, you need to have another way of expressing it.

Documenting two separate endpoints will also be easier for your users. They will usually already know what sort of data they have access to and can pass in to you. Having a clearly separated set of two endpoints with exact specifications will allow them to find the one they are looking for and use that. It will be clearly documented and straightforward to use, instead of trying to understand the type differentiation between two.

| improve this answer | |
  • Today I found one indicator in the HTTP header telling if the given parameter is id or code. With this indicator is the design still bad? – Rui Jun 27 at 13:06
3

My quesiton is whether such endpoint design is a good practice or a typical bad practice?

Same answer as others, different take:

/sites/1/buildings/5
/sites/rake/building/5
/sites/1/buildings/sf
/sites/rake/building/sf

As far as REST is concerned, these resource identifiers are all different; they identify different resources (that happen to share the same time series of representations). In other words, from the client perspective, these are no more related than

/A
/B
/C
/D

The convenience of ambiguity doesn't do anything for the general purpose components.

That ambiguity is really only useful to your clients in a limited circumstance - where they have the identifier and either don't know what it is, or don't care to make the distinction (I'm a human being doing data entry, and I just want to copy entries into the form without needing to navigate to different forms based on the data I'm looking at).

So if you have a business need to support that ambiguity, then yes, you pinch your nose and do it.

But introducing ambiguity just so that you can compress four request handlers into one? That's a design decision that I would not be able to defend in review.

| improve this answer | |
  • 1
    This. #1 avoid ambiguity by using formats for ids and codes that are distinguishable #2 choose a canonical resource location #3 use 307/308 redirects – Bergi Jun 27 at 0:08
  • Today I found one indicator in the HTTP header telling if the given parameter is id or code. With this indicator is the design still bad? – Rui Jun 27 at 13:29
  • That seems pretty weird, so I'm going to guess that the initial assessment of still "not defendable". It's not obvious to me what general purpose header would communicate intended semantics of the target-uri. – VoiceOfUnreason Jun 27 at 17:35
2

It's bad.

The obvious flaw is that I could have siteId: 1 and siteCode: "1", but more generally do I really want to base my routing on whether a variable parses as an int or string?

There is no reason not to have two endpoints, one for each query, which makes your API unambiguous and easier to code.

/sites/1/buildings/5                //default to id
/sitesByCode/rake/building/5       //special endpoints for custom search
/sitesByCode/rake/buildingByCode/rf

Or for a more general case some sort of searchendpoint which accepts a query parameter of some kind

/searchSites?query=siteCode:rake
| improve this answer | |
  • As the site id and building id are generated automatically by the database as a long integer with more than 10 digits, whereas the site code and building code has the constraint of not allowing only numbers but with some format. Is this design still bad? – Rui Jun 26 at 12:53
  • 1
    I'd argue that it is still bad. The most important would be avoiding any direct conflict, but second most is preserving intent and making it easy to understand & code. It might seem easy to understand if they can just pass either, but it is easier to find the endpoint you are looking for, with the types you are looking for, and have the documentation be straightforward. In general, it's good practice to keep them separate, then you'll never forget and have two endpoints with ids/codes that conflict. – Halcyon Jun 26 at 13:17
  • yeah, the "but that will never happen" excuse is great until something changes. You want to be programming defensively, so that you aren't relying on some other unrelated part of the code stopping the bad. – Ewan Jun 26 at 13:21
1

Instead of describing your URL scheme as:

/sites/<site id or site code>/buildings/<building id or building code>

Why not describe it as:

/sites/<site>/buildings/<building>

Some of your designations for sites and buildings being classified as ids respectively codes looks like an internal implementation-detail which nobody cares about, and should thus not be mentioned.

That is, if no id can be mistaken as a code for a different site / building or vice versa.
If that is not guaranteed, there is no choice to make. Though as that ambiguity will bite you outside this specific context too, consider fixing it.

A common example is login prompts:
In addition to the password, you can give your customer-id, telephone-number, or email-address, and you don't have to specify which of the options you chose as they cannot be mistaken for each other.

| improve this answer | |

Not the answer you're looking for? Browse other questions tagged or ask your own question.