mscharhag, Programming and Stuff;

A blog about programming and software development topics, mostly focused on Java technologies including Java EE, Spring and Grails.

Monday, 27 July, 2020

REST: Managing One-To-Many relations

In a previous post we looked at many-to-many relations. This time we will see how to model one-to-many relations in a RESTful API.

An important question here is, if both sides of the relation can exist on their own (similar to typical many-to-many relations) or if the many-side is tightly coupled to the one-side. In the following we will examine both cases with different examples.

Tightly coupled relations

It is quite common for one-to-many relations that the many-side is tightly coupled to the one-side.

For example, consider a relation between articles and comments. An article can have many comments while a comment always belongs to exactly one article. Comments cannot move from one article to another article and the deletion of an article also deletes attached comments.

In such a scenario it is often a good idea to express this type of relation via the resource URI. In this example we can model comments as a sub-resource of articles. For example: /articles/<article-id>/comments. We can then use standard CRUD operations on this sub-resource to create, read, update and delete comments:

Getting all comments of an article 123:

GET /articles/123/comments

Creating a new comment for article 123:

POST /articles/123/comments
Content-Type: application/json
{
    "message": "Foo",
    ...
}

Updating comment 456:

PUT /articles/123/comments/456
Content-Type: application/json
{
    "message": "Bar",
    ...
}

Deleting comment 456:

DELETE /articles/123/comments/456

Here the relation is only expressed by the resource URI. We do not need specific operations to attach or detach a comment to / from an article.

Both sides of the relation can exist on their own

Now let's look at a different example: A relationship between a player and a sports team. A team consists of many players and a player can only play for one team at a time. However, the player can change teams or be without a team for some time.

In this situation we use an approach similar to many-to-many relations. We use two separate resources for players and teams: For example /players and /teams. Both resources can be managed on their own (for example via common CRUD operations).

Next we create a sub-resource for the relation, for example /teams/<team-id>/players. This sub-resource is only used to manage the relation between both resources. We can now use GET, PUT and DELETE operations to retrieve, create and delete relations.

Getting players assigned to team 123:

GET /teams/123/players

Assigning player 42 to team 123:

PUT /teams/123/players/42

Unassigning player 42 from team 123:

DELETE /teams/123/players/42

It is part of the servers logic, to make sure a player is only assigned to a single team. Assume player 42 is currently assigned to team 122. Now, when a PUT /teams/123/players/42 request is issued, the server has first to unassign player 42 from team 122 before he is assigned to team 123. So, this request also modifies the /teams/122/players resource, which should be remembered if a cache is present.

Note we do not need a request body for any of these requests because the sub-resource is only used to manage the relation which can be fully determined by the request URI.

We can also model this from the player-side of the relation. Again, we use a new sub-resource: /players/<player-id>/team.

Getting the current team of player 42:

GET /player/42/team

Assigning player 42 to team 123:

PUT /player/42/team/123

Unassigning player 42 from the current team:

DELETE /player/42/team

Note: For the DELETE request no team id is required (a player can only be in one team).

Summary

We looked into two different approaches of modelling one-to-many relations with a REST API.

If both parts of the relation are tightly coupled we can often express the many-part as a sub-resource of the one-part and use simple CRUD operations. The relation is only expressed via the URI and no special assignment operation is needed.

However, if both sides of the relation can exist on their own, we use two separate resources and add sub-resources to manage the relation.

Comments

  • Hector - Friday, 7 August, 2020

    crystal clear. thanks.

Leave a reply