Imagine you have 2 entities, Player and Team, where players can be on multiple teams. In my data model, I have a table for each entity, and a join table to maintain the relationships. Hibernate is fine at handling this, but how might I expose this relationship in a RESTful API?
I can think of a couple ways. First, I might have each entity contain a list of the other, so a Player object would have a list of Teams it belongs to, and each Team object would have a list of Players that belong to it. So to add a Player to a Team, you would just POST the player's representation to an endpoint, something like POST /player
or POST /team
with the appropriate object as the payload of the request. This seems the most "RESTful" to me but feels a little weird.
/api/team/0:
{
name: 'Boston Celtics',
logo: '/img/Celtics.png',
players: [
'/api/player/20',
'/api/player/5',
'/api/player/34'
]
}
/api/player/20:
{
pk: 20,
name: 'Ray Allen',
birth: '1975-07-20T02:00:00Z',
team: '/api/team/0'
}
The other way I can think of to do this would be to expose the relationship as a resource in its own right. So to see a list of all the players on a given team, you might do a GET /playerteam/team/{id}
or something like that and get back a list of PlayerTeam entities. To add a player to a team, POST /playerteam
with an appropriately built PlayerTeam entity as the payload.
/api/team/0:
{
name: 'Boston Celtics',
logo: '/img/Celtics.png'
}
/api/player/20:
{
pk: 20,
name: 'Ray Allen',
birth: '1975-07-20T02:00:00Z',
team: '/api/team/0'
}
/api/player/team/0/:
[
'/api/player/20',
'/api/player/5',
'/api/player/34'
]
What is the best practice for this?
Make a separate set of /memberships/
resources.
/teams/3/players/
that list will be invalidated, but you don't want the alternate URL /players/5/teams/
to remain cached. Yes, different caches will have copies of each list with different ages, and there's not much we can do about that, but we can at least minimize the confusion for the user POST'ing the update by limiting the number of entities we need to invalidate in their client's local cache to one and only one at /memberships/98745
(see Helland's discussion of "alternate indices" in Life beyond Distributed Transactions for a more detailed discussion)./players/5/teams
or /teams/3/players
(but not both). Let's assume the former. At some point, however, you will want to reserve /players/5/teams/
for a list of current memberships, and yet be able to refer to past memberships somewhere. Make /players/5/memberships/
a list of hyperlinks to /memberships/{id}/
resources, and then you can add /players/5/past_memberships/
when you like, without having to break everyone's bookmarks for the individual membership resources. This is a general concept; I'm sure you can imagine other similar futures which are more applicable to your specific case.