DEV Community

RESTful API design concerns

Marcin Piczkowski on July 26, 2019

-- Photo by Omer Rana on Unsplash When working on real production system recently I asked myself a question: Should REST API be constrained ...
Collapse
 
sql_knievel profile image
Rich Boniface • Edited

We can all picture what a “car” represents, but what does a “spot” mean in this context? A physical parking spot? If so, think about the real relationship between them as entities. I’d argue that a car can exist without a spot (what if it’s out being driven and another car gets put in the previous spot?) and that a spot can exist without a car (the car that used to be in that spot got crashed, now what?)

If each one can exist in your system without the other, then I think there’s no real hierarchy between them and having both a /car/{carid} and a /spot/{spotid} endpoint makes more sense.

Collapse
 
herifauzan profile image
heri fauzan • Edited

Why don't use grapql instead.. You only need to design the query.. Not the endpoints..

Collapse
 
piczmar_0 profile image
Marcin Piczkowski

Sure, but my post refers to REST API, of course you can use different technologies where you don't have this problem, but sometimes you're constrained to use REST.

Collapse
 
piczmar_0 profile image
Marcin Piczkowski

Maybe I was not precise enough, please see my answer above.

Collapse
 
hugecoderguy profile image
Christian Kreiling

I've been dealing with this lately with regards to shared user profiles. A user can be a member of any profile so long as they are invited, and can perform CRUD operations on entities owned by the profile.

Profiles have private items with all the standard CRUD ops, so I've gone with the "long approach:" /profiles/:profile_id/items/:item_id. In my case, I think this is semantically correct because a user should be operating in the context of a profile. If the given profile tries to update an item owned by another profile, I issue an unauthorized response informing the user of the API that the given profile doesn't own the item.

The short approach, on the other hand, has no sense of profile context. Though there are plenty of cases where the nested resources don't make sense, I think they are especially nice when a user of your API operates under a given context, such as a profile

Collapse
 
piczmar_0 profile image
Marcin Piczkowski

I've been digging more and encountered this post from Google which also has a section mentioning the concern I described in my post as well as many other interesting ideas. You may find it worth reading too :)

Collapse
 
evgenykhaliper profile image
Evgeny Khaliper

Had same thoughts when started new product few weeks ago. Ended up with exposing sub object without the root eventhough the root can be extracted is like a postcode without an actual address to the mailman or the recipient. APIs are for people.

Collapse
 
jeastham1993 profile image
James Eastham

Either option is entirely logic and as a developer using your API I would understand both use cases. As long as it's well documented then both options are perfectly valid.

That said, spots and cars are seperate entities that could exist without each other. If you wanted to know where a car was at any given time then having spot id in the request to get that info seems strange.

I'd probably go for car/{carid} and spot/{spotId}

Collapse
 
piczmar_0 profile image
Marcin Piczkowski

By saying in post that car is a part of a spot I mean that you cannot rent the same car in other spot or park it there as each spot is managed by completely independent company, same way you have flats for rent on Airbnb.

Collapse
 
jeastham1993 profile image
James Eastham

Aha! I see, sorry my misunderstanding.

Collapse
 
nogtini profile image
Joey D. • Edited

Generally the concept of a resource should be fully decoupled from any middleware that generates the representation of that resource. A resource is a concept, and, while the representation is dependent on your architecture, the resource should remain conceptually independent and coupled only to the problem domain.

Additionally, it's recommended REST practice to try to make your URIs as still and unchanging as possible, as the concept of the resource should change very slowly if at all (see "Cool URIs Don't Change" w3.org/Provider/Style/URI). The representation of this resource can change as often as it want so long as it conceptually equivalent to the original resource.

Collapse
 
piczmar_0 profile image
Marcin Piczkowski

Yes, this makes also a lot of sense. This reason would probably win if I am doing a publicaly available API.

Collapse
 
anandchowdhary profile image
Anand Chowdhary

I had the exact same thought because I had users, memberships, and organizations. Each membership connects a user with an organization, so each org can have multiple members and each user can be part of multiple orgs.

The basic RESTs are easy:
GET /users/:id/memberships
GET /organizations/:id/memberships

But it’s the same problem when thinking of a single membership:

You could GET /memberships/:id or have two endpoints, GET /users/:id/memberships/:mid and GET /organizations/:id/memberships/:mid. We chose the second, longer one and added the additional condition in the query because it felt semantically logical.

Collapse
 
drdamour profile image
chris damour

Discussing href structure has nothing to do with RESTful architecture. To be restful you need to be discussing the hypermedia controls between resources. URLs are irrelevant in RESTful.

Collapse
 
piczmar_0 profile image
Marcin Piczkowski

It depends.

As far as I know there are 4 different maturity levels of REST according to Richardson. You're probably referring to 3rd level also known as HATEAOS. In practice I have never worked in project doing the API this way. Even big companies like FB or Google does not build their APIs this way because in most cases API clients don't need this kind of intelligence. So if your client needs this, then OK, otherwise it just adds too much work overhead and clutters the API.

Usually what I do is the 2nd level or maturity so I would not agree with your saying.

Moreover, please note that I am discussing href structure in context of service architecture (backend app design), not the REST architecture (multimedia headers, http methods, maturity).

Collapse
 
drdamour profile image
chris damour

It does not depend. Fielding gave one definition of rest despite what Richardson says. But EVEN if we take Richardson, not a single level mentions href structure so again..this has nothing to do with RESTful at all.

Google and fb build tons of RESTful services. Their entire html applications are RESTful. You are way off track here.

there is no such distinction between restful architecture and backend design as you say. RESTful goes from service to client, that’s a core tenant of its very definition. Try to think about your problem restfully and you’ll soon see why your questions are so confusing now. Your trying to model your problem domain with a broken language.

Thread Thread
 
piczmar_0 profile image
Marcin Piczkowski

Ok, we have different opinions and I appreciate it. So what's the right API for my problem domain according to you and can you justify it?

Thread Thread
 
drdamour profile image
chris damour

That’s a fair question, lemme think about it today

Collapse
 
rodrigoimas profile image
rodrigoimas • Edited

Instead of focusing so much on the architecture constraints, I would concentrate on defining common use cases for my API. If a use case makes sense for my business, there is a big chance I will need to change your architecture, your db design or both.
Conceptually speaking, API resource ids should be decoupled from your legacy data models. Otherwise your API will eventually be limited and there would be not much room for API new versions.
In order to enforce that decoupling, when designing a new API, I always consider building an intermediate data model for my APIs that can bind resource ids with legacy apps ids.

Collapse
 
piczmar_0 profile image
Marcin Piczkowski

Mostly I agree with what you wrote except decoupling API IDs from data model IDs. I've never seen this in practice. How would you achieve this? e.g. if I have a UUID in my DB record how would you translate it to API ID?

Collapse
 
dbanty profile image
Dylan Anthony

I like the shorter URL for a couple reasons:

  1. It’s easier to read, therefore easier to use
  2. The spot part of the url makes me think I’m pulling the car details relative to that spot. In other words, pulling the spot/car relationship, not the car itself.
  3. The longer URL implies that there may be a many to many relationship, that multiple spots may be valid for that car. Otherwise why specify the spot?
  4. I would argue that the simpler URL is more flexible. If you add different use cases for the car, you can add them on in relationship URLs. But the car will always refer to the row in the car table, as it should.
Collapse
 
taufik_nurrohman profile image
Taufik Nurrohman

I would add a version path just to make sure that the newer API will not break older users.

/v1/spots/{spotId}/cars/{carId}

Collapse
 
piczmar_0 profile image
Marcin Piczkowski

Thanks for pointing this out. I prefer versioning using headers instead of path. In the initial release I do not require any version headers and then try to maintain API changes so that they are backward compatible as long as possible. Finally, if this is really a must to version the API then I would add version headers.

Collapse
 
steelwolf180 profile image
Max Ong Zong Bao • Edited

It depends I'm more towards your API should focus on use cases and general API best practices to make it easy for anyone to use your API.

Better documentation always doesn't hurt

Collapse
 
piczmar_0 profile image
Marcin Piczkowski

Encountered this interesting podcast about RESTful APIs which is related to this discussion.