Rican7
Rican7

Reputation: 1143

What's the best way of implementing a many-to-many relationship, that contains meta, RESTfully?

When representing data models through a RESTful interface it is understood to create top level endpoints that associate to a type/group of objects:

We can re-use HTTP verbs to enable actions upon these groups (GET to list, POST to create, etc). And when representing a model with a "dependency" (being that it can't exist without a "parent"), we can create deeper endpoints to represent that dependency relationship:

In this case, it makes sense to not have a top-level endpoint of /tokens, as they shouldn't be able to exist without the user.

Many-to-many relationships get a bit more tricky. If two models can have a many-to-many relationship but can also truly exist on their own, it makes sense to give both objects a top-level endpoint and a deeper endpoint for defining that relationship:

We can then use PUT and DELETE methods to define those relationships through an HTTP interface: PUT /users/[:user_id]/cars/[:car_id]. It makes sense that running that PUT operation would create a data-model that somehow links the two objects (like a join table in a relational DB).


The tricky part, then, becomes deciding on where to limit the interface to combat redundancy.

Do you allow a GET request to the second-level deep endpoints (GET /users/[:user_id]/cars/[:car_id])? Or do you require that they access the "car" from the top level GET /cars/[:id]?

Now, what if the many-to-many relationship contains meta information? How do you represent that and where do you return it?

For example, what if we wanted to keep track of how many times a user drove a certain car? Where would we return that information? If we return it at the nested endpoint, are we violating REST or being inconsistent if we return the meta information and not the resource? Do we embed the meta information in the requested resource through an attribute of some kind?

Pls advise. :P (but really, thanks)

Upvotes: 4

Views: 149

Answers (2)

Ben Edmunds
Ben Edmunds

Reputation: 878

This is really more of a personal design preference at this point IMHO.

I would personally opt for stopping at /users/[:user_id]/cars/ and then requiring a call to /cars/[:car_id] to get the car information.

If you are including relation specific metadata though, like "how many times a user drove a certain car?" it would make sense to keep that under a deeper relationship like /users/[:user_id]/cars/[:car_id].

Truthfully it's not an exact science. You have to do what is simplest, most expressive, yet still powerful enough for your data model.

Upvotes: 3

Jeremy Weiskotten
Jeremy Weiskotten

Reputation: 81

You could create a new resource. Something like users/[:user_id]/cars/[:car_id]/stats, whose response includes {drivings_count: 123}. You'd probably only allow a GET of this resource.

Upvotes: 3

Related Questions