Best way to model freindship relationship in couchdb - nosql

I am writing a social networking web app with couchdb as backend. Currently I am maintaining user profiles as JSON docs.
My app has a friendship feature when one user will request other user and upon acceptance the friendship is solemnized. Besides friendship there is one way relationship called "follow" relationship as well.
I thought of creating a connection doc
{
source_user:'',
target_user:'',
source_follows_target:'',
target_follows_source:'',
..Similarly for friendship...
}
This doesn't look right to me at all. Since the relationship exists between two exactly similar entities (users in this case) I dont thin the model should try to distinguish between source and target.

The fact that the relationship can be (or is always) symmetric doesn't mean it necessarily has to be modelled as one logical relationship. I think it might more general to model the possibility of either, and then prevent one-way friendships in your application if you so desire.
In which case for each user you might have a set of users that they consider their friend (One -> Many). You could store a copy (or cache) of whether it is symmetric or not on each of these relationship objects to make it a bit more scalable.
A rough example of how a user object might look:
{
"userId": 1,
"friends": [{"userId": 2, "friendsBack": true | false}, ...]
}
Operations on these sets of users, e.g. intersections (friends in common), would then be a lot easier since they are accessed directly from the user object.

Related

Foreign Key in Rest API design [duplicate]

Imagine you have two 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 of 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.
REST is about making evolvable systems if nothing else. At this moment, you may only care that a given player is on a given team, but at some point in the future, you will want to annotate that relationship with more data: how long they've been on that team, who referred them to that team, who their coach is/was while on that team, etc etc.
REST depends on caching for efficiency, which requires some consideration for cache atomicity and invalidation. If you POST a new entity to /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).
You could implement the above 2 points by simply choosing /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.
In a RESTful interface, you can return documents that describe the relationships between resources by encoding those relationships as links. Thus, a team can be said to have a document resource (/team/{id}/players) that is a list of links to players (/player/{id}) on the team, and a player can have a document resource (/player/{id}/teams) that is a list of links to teams that the player is a member of. Nice and symmetric. You can the map operations on that list easily enough, even giving a relationship its own IDs (arguably they'd have two IDs, depending on whether you're thinking about the relationship team-first or player-first) if that makes things easier. The only tricky bit is that you've got to remember to delete the relationship from the other end as well if you delete it from one end, but rigorously handling this by using an underlying data model and then having the REST interface be a view of that model is going to make that easier.
Relationship IDs probably ought to be based on UUIDs or something equally long and random, irrespective of whatever type of IDs you use for teams and players. That will let you use the same UUID as the ID component for each end of the relationship without worrying about collisions (small integers do not have that advantage). If these membership relationships have any properties other than the bare fact that they relate a player and a team in a bidirectional fashion, they should have their own identity that is independent of both players and teams; a GET on the player»team view (/player/{playerID}/teams/{teamID}) could then do an HTTP redirect to the bidirectional view (/memberships/{uuid}).
I recommend writing links in any XML documents you return (if you happen to be producing XML of course) using XLink xlink:href attributes.
I would map such relationship with sub-resources, general design/traversal would then be:
# team resource
/teams/{teamId}
# players resource
/players/{playerId}
# teams/players subresource
/teams/{teamId}/players/{playerId}
In RESTful-terms it helps a lot in not thinking of SQL and joins, but more into collections, sub-collections and traversal.
Some examples:
# getting player 3 who is on team 1
# or simply checking whether player 3 is on that team (200 vs. 404)
GET /teams/1/players/3
# getting player 3 who is also on team 3
GET /teams/3/players/3
# adding player 3 also to team 2
PUT /teams/2/players/3
# getting all teams of player 3
GET /players/3/teams
# withdraw player 3 from team 1 (appeared drunk before match)
DELETE /teams/1/players/3
# team 1 found a replacement, who is not registered in league yet
POST /players
# from payload you get back the id, now place it officially to team 1
PUT /teams/1/players/44
As you see, I don't use POST for placing players to teams, but PUT, which handles your n:n relationship of players and teams better.
My preferred solution is to create three resources: Players, Teams and TeamsPlayers.
So, to get all the players of a team, just go to Teams resource and get all its players by calling GET /Teams/{teamId}/Players.
On the other hand, to get all the teams a player has played, get the Teams resource within the Players. Call GET /Players/{playerId}/Teams.
And, to get the many-to-many relationship call GET /Players/{playerId}/TeamsPlayers or GET /Teams/{teamId}/TeamsPlayers.
Note that, in this solution, when you call GET /Players/{playerId}/Teams, you get an array of Teams resources, that is exactly the same resource you get when you call GET /Teams/{teamId}. The reverse follows the same principle, you get an array of Players resources when call GET /Teams/{teamId}/Players.
In either calls, no information about the relationship is returned. For example, no contractStartDate is returned, because the resource returned has no info about the relationship, only about its own resource.
To deal with the n-n relationship, call either GET /Players/{playerId}/TeamsPlayers or GET /Teams/{teamId}/TeamsPlayers. These calls return the exactly resource, TeamsPlayers.
This TeamsPlayers resource has id, playerId, teamId attributes, as well as some others to describe the relationship. Also, it has the methods necessary to deal with them. GET, POST, PUT, DELETE etc that will return, include, update, remove the relationship resource.
The TeamsPlayers resource implements some queries, like GET /TeamsPlayers?player={playerId} to return all TeamsPlayers relationships the player identified by {playerId} has. Following the same idea, use GET /TeamsPlayers?team={teamId} to return all the TeamsPlayers that have played in the {teamId} team.
In either GET call, the resource TeamsPlayers is returned. All the data related to the relationship is returned.
When calling GET /Players/{playerId}/Teams (or GET /Teams/{teamId}/Players), the resource Players (or Teams) calls TeamsPlayers to return the related teams (or players) using a query filter.
GET /Players/{playerId}/Teams works like this:
Find all TeamsPlayers that the player has id = playerId. (GET /TeamsPlayers?player={playerId})
Loop the returned TeamsPlayers
Using the teamId obtained from TeamsPlayers, call GET /Teams/{teamId} and store the returned data
After the loop finishes. Return all teams that were got in the loop.
You can use the same algorithm to get all players from a team, when calling GET /Teams/{teamId}/Players, but exchanging teams and players.
My resources would look like this:
/api/Teams/1:
{
id: 1
name: 'Vasco da Gama',
logo: '/img/Vascao.png',
}
/api/Players/10:
{
id: 10,
name: 'Roberto Dinamite',
birth: '1954-04-13T00:00:00Z',
}
/api/TeamsPlayers/100
{
id: 100,
playerId: 10,
teamId: 1,
contractStartDate: '1971-11-25T00:00:00Z',
}
This solution relies on REST resources only. Although some extra calls may be necessary to get data from players, teams or their relationship, all HTTP methods are easily implemented. POST, PUT, DELETE are simple and straightforward.
Whenever a relationship is created, updated or deleted, both Players and Teams resources are automatically updated.
The existing answers don't explain the roles of consistency and idempotency - which motivate their recommendations of UUIDs/random numbers for IDs and PUT instead of POST.
If we consider the case where we have a simple scenario like "Add a new player to a team", we encounter consistency issues.
Because the player doesn't exist, we need to:
POST /players { "Name": "Murray" } //=> 201 /players/5
POST /teams/1/players/5
However, should the client operation fail after the POST to /players, we've created a player that doesn't belong to a team:
POST /players { "Name": "Murray" } //=> 201 /players/5
// *client failure*
// *client retries naively*
POST /players { "Name": "Murray" } //=> 201 /players/6
POST /teams/1/players/6
Now we have an orphaned duplicate player in /players/5.
To fix this we might write custom recovery code that checks for orphaned players that match some natural key (e.g. Name). This is custom code that needs to be tested, costs more money and time etc etc
To avoid needing custom recovery code, we can implement PUT instead of POST.
From the RFC:
the intent of PUT is idempotent
For an operation to be idempotent, it needs to exclude external data such as server-generated id sequences. This is why people are recommending both PUT and UUIDs for Ids together.
This allows us to rerun both the /players PUT and the /memberships PUT without consequences:
PUT /players/23lkrjrqwlej { "Name": "Murray" } //=> 200 OK
// *client failure*
// *client YOLOs*
PUT /players/23lkrjrqwlej { "Name": "Murray" } //=> 200 OK
PUT /teams/1/players/23lkrjrqwlej
Everything is fine and we didn't need to do anything more than retry for partial failures.
This is more of an addendum to the existing answers but I hope it puts them in context of the bigger picture of just how flexible and reliable ReST can be.
I know that there's an answer marked as accepted for this question, however, here is how we could solve the previously raised issues:
Let's say for PUT
PUT /membership/{collection}/{instance}/{collection}/{instance}/
As an example, the followings will all result in the same effect without a need for syncing because they are done on a single resource:
PUT /membership/teams/team1/players/player1/
PUT /membership/players/player1/teams/team1/
now if we want to update multiple memberships for one team we could do as follows (with proper validations):
PUT /membership/teams/team1/
{
membership: [
{
teamId: "team1"
playerId: "player1"
},
{
teamId: "team1"
playerId: "player2"
},
...
]
}
/players (is a master resource)
/teams/{id}/players (is a relationship resource, so it react diferent that 1)
/memberships (is a relationship but semantically complicated)
/players/memberships (is a relationship but semantically complicated)
I prefer 2

How would one structure a mutual relationship between two users in Parse?

I am playing around with my own Parse server and would like to build a social media app just for fun. Just learning how to structure it and get everything developed.
One of the problems that I am having trouble wrapping my head around is how I would create a relationship structure between two users where the connection is mutual.
You would have to, on both ends, accept the relationship request.
I would also like to know how I would query for the friends list. Which would how would I query for the people the user has mutual connections with?
I hear that a friendship table relationship would be helpful for structuring a relationship with friends. But I would like to make it a mandatory mutual connection for people to be friends.
All you actually need is Request(person, friend) "PERSON asked to friend FRIEND", because requited and unrequited friendships can be extracted by query. You might prefer to keep those two relationships separately instead: Requited(person, friend) "PERSON asked to friend FRIEND and FRIEND asked to friend PERSON" & Unrequited(person, friend) "PERSON asked to friend FRIEND and FRIEND hasn't asked to friend PERSON". If a row (p, f) is in Requited then so is (f, p), and neither is in Unrequited. The two tables UNION to Request. But then you need to move rows between the tables when people friend or unfriend others. What is "best" logically depends on patterns of update & querying, and there will be likely be another "best" when implementation is included.
Database design is the identification of sufficient relationships/associations to describe any application situation. Each gets a table whose rows make a true statement from some characterizing statement template, aka predicate. Each query returns the rows that satisfy its own relationship/association, characterized by a predicate built from base predicates. Normalization helps with relationship/association/predicate/table choice, and so do information modeling methods. Pick a method (eg Object-Role Modeling) and learn it & how & why it leads to "good" designs while avoiding "bad" ones.
Create a new Object call "Relationships" and store both PFUser id's.

RESTful design: when to use sub-resources? [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 4 years ago.
Improve this question
When designing resource hierarchies, when should one use sub-resources?
I used to believe that when a resource could not exist without another, it should be represented as its sub-resource. I recently ran across this counter-example:
An employee is uniquely identifiable across all companies.
An employee's access control and life-cycle depend on the company.
I modeled this as: /companies/{companyName}/employee/{employeeId}
Notice, I don't need to look up the company in order to locate the employee, so should I? If I do, I'm paying a price to look up information I don't need. If I don't, this URL mistakenly returns HTTP 200:
/companies/{nonExistingName}/employee/{existingId}
How should I represent the fact that a resource to belongs to another?
How should I represent the fact that a resource cannot be identified without another?
What relationships are sub-resources meant and not meant to model?
A year later, I ended with the following compromise (for database rows that contain a unique identifier):
Assign all resources a canonical URI at the root (e.g. /companies/{id} and /employees/{id}).
If a resource cannot exist without another, it should be represented as its sub-resource; however, treat the operation as a search engine query. Meaning, instead of carrying out the operation immediately, simply return HTTP 307 ("Temporary redirect") pointing at the canonical URI. This will cause clients to repeat the operation against the canonical URI.
Your specification document should only expose root resources that match your conceptual model (not dependent on implementation details). Implementation details might change (your rows might no longer be unique identifiable) but your conceptual model will remain intact. In the above example, you'd tell clients about /companies but not /employees.
This approach has the following benefits:
It eliminates the need to do unnecessary database look-ups.
It reduces the number of sanity-checks to one per request. At most, I have to check whether an employee belongs to a company, but I no longer have to do two validation checks for /companies/{companyId}/employees/{employeeId}/computers/{computerId}.
It has a mixed impact on database scalability. On the one hand you are reducing lock contention by locking less tables, for a shorter period of time. But on the other hand, you are increasing the possibility of deadlocks because each root resource must use a different locking order. I have no idea whether this is a net gain or loss but I take comfort in the fact that database deadlocks cannot be prevented anyway and the resulting locking rules are simpler to understand and implement. When in doubt, opt for simplicity.
Our conceptual model remains intact. By ensuring that the specification document only exposes our conceptual model, we are free to drop URIs containing implementation details in the future without breaking existing clients. Remember, nothing prevents you from exposing implementation details in intermediate URIs so long as your specification declares their structure as undefined.
This is problematic because it's no longer obvious that a user belongs
to a particular company.
Sometimes this may highlight a problem with your domain model. Why does a user belong to a company? If I change companies, am I whole new person? What if I work for two companies? Am I two different people?
If the answer is yes, then why not take some company-unique identifier to access a user?
e.g. username:
company/foo/user/bar
(where bar is my username that is unique within that specific company namespace)
If the answer is no, then why am I not a user (person) by myself, and the company/users collection merely points to me: <link rel="user" uri="/user/1" /> (note: employee seems to be more appropriate)
Now outside of your specific example, I think that resource-subresource relationships are more appropriate when it comes to use rather than ownership (and that's why you're struggling with the redundancy of identifying a company for a user that implicitly identifies a company).
What I mean by this is that users is actually a sub-resource of a company resource, because the use is to define the relationship between a company and its employees - another way of saying that is: you have to define a company before you can start hiring employees. Likewise, a user (person) has to be defined (born) before you can recruit them.
Your rule to decide if a resource should be modeled as sub resource is valid. Your problem does not arise from a wrong conceptual model but you let leak your database model into your REST model.
From a conceptual view an employee if it can only exist within a company relationship is modeled as a composition. The employee could be thus only identified via the company. Now databases come into play and all employee rows get a unique identifier.
My advice is don't let the database model leak in your conceptional model because you're exposing infrastructure concerns to your API. For example what happens when you decide to switch to a document oriented database like MongoDB where you could model your employees as part of the company document and no longer has this artificial unique id? Would you want to change your API?
To answer your extra questions
How should I represent the fact that a resource to belongs to another?
Composition via sub resources, other associations via URL links.
How should I represent the fact that a resource cannot be identified without another?
Use both id values in your resource URL and make sure not to let your database leak into your API by checking if the "combination" exists.
What relationships are sub-resources meant and not meant to model?
Sub resources are well suited for compositions but more generally spoken to model that a resource cannot exist without the parent resource and always belongs to one parent resource. Your rule when a resource could not exist without another, it should be represented as its sub-resource is a good guidance for this decision.
if a subresource is uniquely identifiable without its owning entity, it is no subresource and should have its own namespace (i.e. /users/{user} rather than /companies/{*}/users/{user}).
Most importantly: never ever ever everer uses your entity's database primary key as the resource identifier. that's the most common mistake where implementation details leak to the outside world. you should always have a natural business key (like username or company-number, rather than user-id or company-id). the uniqueness of such a key can be enforced by a unique constraint, if you wish, but the primary key of an entity should never ever everer leave the persistence-layer of your application, or at least it should never be an argument to any service method. If you go by this rule, you shouldn't have any trouble distinguishing between compositions (/companies/{company}/users/{user}) and associations (/users/{user}), because if your subresource doesn't have a natural business key, that identifies it in a global context, you can be very certain it really is a depending subresource (or you must first create a business key to make it globally identifiable).
This is one way you can resolve this situation:
/companies/{companyName}/employee/{employeeId} -> returns data about an employee, should also include the person's data
/person/{peopleId} -> returns data about the person
Talking about employee makes no sense without also talking about the company, but talking about the person does make sense even without a company and even if he's hired by multiple companies. A person's existence is independent of whether he's hired by any companies, but an employment's existence does depend on the company.
The issue seems to be when there is no specific company but an employee technically belongs to some company or organization otherwise they could be called bums or politicians. Being an employee implies a company/organization relationship somewhere but not a specific one. Also employees can work for more than one company/organization. When the specific company context is required then your original works /companies/{companyName}/users/{id}
Lets say you want to know the EmployerContribution for your ira/rsp/pension you'd use:
/companies/enron/users/fred/EmployerContribution
You get the specific amount contributed by enron (or $0).
What if you want the EmployerContributions from any or all companies fred works(ed) for? You don't need a concrete company for it to make sense. /companies/any/employee/fred/EmployerContribution
Where "any" is obviously an abstraction or placeholder when the employee's company doesn't matter but being an employee does. You need to intercept the 'company" handler to prevent a db lookup (although not sure why company wouldn't be cached? how many can there be?)
You can even change the abstraction to represent something like all companies for which Fred was employed over the last 10 years.
/companies/last10years/employee/fred/EmployerContribution

Nested resource paths when there are multiple associations with different names to the same associated model/table

Having trouble coming up with a good URL path for the case in which the same RESTful resource (model) is associated in two or more different ways with another RESTful resource, e.g.:
Specify a URL path that intuitively represents a request to look up all instances of training sessions associated with a certain trainer, where the TrainingSession model has a trainer_id that is an association to the Employee model.
Specify a URL path that intuitively represents a request to look up all instances of training sessions associated with a certain trainee, where the TrainingSession model has a trainee_id that is also an association to the Employee model.
Including "trainees" or "trainers" in the path of the URL doesn't seem to be right, since they aren't real resources (only TrainingSession and Employee are):
/trainees/1/training_sessions
/trainers/1/training_sessions
But, using "employees" is too ambiguous and could mean either:
/employees/1/training_sessions
What would you suggest for these routes/paths and why?
Actually using trainees and trainers is not wrong at all.
Your trainees and trainers both urls can map to the employee controller function which then find the trainee / trainer data using the id provided. I don't see why you could not do that. It is simply smart url directing.
If you had to store information about trainees and trainers seperately then they need their own model. But if TrainingSession has trainee id and trainer id which maps directly to employee ids and you don't have to store anything else about the trainee and trainer I don't see why the mentioned approach is not usable.
EDIT: The following is a matter of philosophy and therefore largely debatable.
I feel the REST principles is for the API user. So as long as user sees that there is a trainee resource and trainer resource and can manipulate it it is REST. How you implement inside, say using MVC is your choice. A good way would be to use MVC principles but principles are guidelines and no golden rules can be applied to all situations. I think creating virtual resources is just fine and they don't have to map to models / controllers of their own.
I would see training sessions as another resource and you are searching training sessions based on search parameters searchType and employeeId. So a #GET on /trainingsessions?searchby=trainer&employeeId=xyz should give sessions for the trainer/trainingsessions?searchby=trainee&employeeId=xyz should give sessions for the trainee.

How can I handle many-to-many relationships in a RESTful API?

Imagine you have two 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 of 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.
REST is about making evolvable systems if nothing else. At this moment, you may only care that a given player is on a given team, but at some point in the future, you will want to annotate that relationship with more data: how long they've been on that team, who referred them to that team, who their coach is/was while on that team, etc etc.
REST depends on caching for efficiency, which requires some consideration for cache atomicity and invalidation. If you POST a new entity to /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).
You could implement the above 2 points by simply choosing /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.
In a RESTful interface, you can return documents that describe the relationships between resources by encoding those relationships as links. Thus, a team can be said to have a document resource (/team/{id}/players) that is a list of links to players (/player/{id}) on the team, and a player can have a document resource (/player/{id}/teams) that is a list of links to teams that the player is a member of. Nice and symmetric. You can the map operations on that list easily enough, even giving a relationship its own IDs (arguably they'd have two IDs, depending on whether you're thinking about the relationship team-first or player-first) if that makes things easier. The only tricky bit is that you've got to remember to delete the relationship from the other end as well if you delete it from one end, but rigorously handling this by using an underlying data model and then having the REST interface be a view of that model is going to make that easier.
Relationship IDs probably ought to be based on UUIDs or something equally long and random, irrespective of whatever type of IDs you use for teams and players. That will let you use the same UUID as the ID component for each end of the relationship without worrying about collisions (small integers do not have that advantage). If these membership relationships have any properties other than the bare fact that they relate a player and a team in a bidirectional fashion, they should have their own identity that is independent of both players and teams; a GET on the player»team view (/player/{playerID}/teams/{teamID}) could then do an HTTP redirect to the bidirectional view (/memberships/{uuid}).
I recommend writing links in any XML documents you return (if you happen to be producing XML of course) using XLink xlink:href attributes.
I would map such relationship with sub-resources, general design/traversal would then be:
# team resource
/teams/{teamId}
# players resource
/players/{playerId}
# teams/players subresource
/teams/{teamId}/players/{playerId}
In RESTful-terms it helps a lot in not thinking of SQL and joins, but more into collections, sub-collections and traversal.
Some examples:
# getting player 3 who is on team 1
# or simply checking whether player 3 is on that team (200 vs. 404)
GET /teams/1/players/3
# getting player 3 who is also on team 3
GET /teams/3/players/3
# adding player 3 also to team 2
PUT /teams/2/players/3
# getting all teams of player 3
GET /players/3/teams
# withdraw player 3 from team 1 (appeared drunk before match)
DELETE /teams/1/players/3
# team 1 found a replacement, who is not registered in league yet
POST /players
# from payload you get back the id, now place it officially to team 1
PUT /teams/1/players/44
As you see, I don't use POST for placing players to teams, but PUT, which handles your n:n relationship of players and teams better.
My preferred solution is to create three resources: Players, Teams and TeamsPlayers.
So, to get all the players of a team, just go to Teams resource and get all its players by calling GET /Teams/{teamId}/Players.
On the other hand, to get all the teams a player has played, get the Teams resource within the Players. Call GET /Players/{playerId}/Teams.
And, to get the many-to-many relationship call GET /Players/{playerId}/TeamsPlayers or GET /Teams/{teamId}/TeamsPlayers.
Note that, in this solution, when you call GET /Players/{playerId}/Teams, you get an array of Teams resources, that is exactly the same resource you get when you call GET /Teams/{teamId}. The reverse follows the same principle, you get an array of Players resources when call GET /Teams/{teamId}/Players.
In either calls, no information about the relationship is returned. For example, no contractStartDate is returned, because the resource returned has no info about the relationship, only about its own resource.
To deal with the n-n relationship, call either GET /Players/{playerId}/TeamsPlayers or GET /Teams/{teamId}/TeamsPlayers. These calls return the exactly resource, TeamsPlayers.
This TeamsPlayers resource has id, playerId, teamId attributes, as well as some others to describe the relationship. Also, it has the methods necessary to deal with them. GET, POST, PUT, DELETE etc that will return, include, update, remove the relationship resource.
The TeamsPlayers resource implements some queries, like GET /TeamsPlayers?player={playerId} to return all TeamsPlayers relationships the player identified by {playerId} has. Following the same idea, use GET /TeamsPlayers?team={teamId} to return all the TeamsPlayers that have played in the {teamId} team.
In either GET call, the resource TeamsPlayers is returned. All the data related to the relationship is returned.
When calling GET /Players/{playerId}/Teams (or GET /Teams/{teamId}/Players), the resource Players (or Teams) calls TeamsPlayers to return the related teams (or players) using a query filter.
GET /Players/{playerId}/Teams works like this:
Find all TeamsPlayers that the player has id = playerId. (GET /TeamsPlayers?player={playerId})
Loop the returned TeamsPlayers
Using the teamId obtained from TeamsPlayers, call GET /Teams/{teamId} and store the returned data
After the loop finishes. Return all teams that were got in the loop.
You can use the same algorithm to get all players from a team, when calling GET /Teams/{teamId}/Players, but exchanging teams and players.
My resources would look like this:
/api/Teams/1:
{
id: 1
name: 'Vasco da Gama',
logo: '/img/Vascao.png',
}
/api/Players/10:
{
id: 10,
name: 'Roberto Dinamite',
birth: '1954-04-13T00:00:00Z',
}
/api/TeamsPlayers/100
{
id: 100,
playerId: 10,
teamId: 1,
contractStartDate: '1971-11-25T00:00:00Z',
}
This solution relies on REST resources only. Although some extra calls may be necessary to get data from players, teams or their relationship, all HTTP methods are easily implemented. POST, PUT, DELETE are simple and straightforward.
Whenever a relationship is created, updated or deleted, both Players and Teams resources are automatically updated.
The existing answers don't explain the roles of consistency and idempotency - which motivate their recommendations of UUIDs/random numbers for IDs and PUT instead of POST.
If we consider the case where we have a simple scenario like "Add a new player to a team", we encounter consistency issues.
Because the player doesn't exist, we need to:
POST /players { "Name": "Murray" } //=> 201 /players/5
POST /teams/1/players/5
However, should the client operation fail after the POST to /players, we've created a player that doesn't belong to a team:
POST /players { "Name": "Murray" } //=> 201 /players/5
// *client failure*
// *client retries naively*
POST /players { "Name": "Murray" } //=> 201 /players/6
POST /teams/1/players/6
Now we have an orphaned duplicate player in /players/5.
To fix this we might write custom recovery code that checks for orphaned players that match some natural key (e.g. Name). This is custom code that needs to be tested, costs more money and time etc etc
To avoid needing custom recovery code, we can implement PUT instead of POST.
From the RFC:
the intent of PUT is idempotent
For an operation to be idempotent, it needs to exclude external data such as server-generated id sequences. This is why people are recommending both PUT and UUIDs for Ids together.
This allows us to rerun both the /players PUT and the /memberships PUT without consequences:
PUT /players/23lkrjrqwlej { "Name": "Murray" } //=> 200 OK
// *client failure*
// *client YOLOs*
PUT /players/23lkrjrqwlej { "Name": "Murray" } //=> 200 OK
PUT /teams/1/players/23lkrjrqwlej
Everything is fine and we didn't need to do anything more than retry for partial failures.
This is more of an addendum to the existing answers but I hope it puts them in context of the bigger picture of just how flexible and reliable ReST can be.
I know that there's an answer marked as accepted for this question, however, here is how we could solve the previously raised issues:
Let's say for PUT
PUT /membership/{collection}/{instance}/{collection}/{instance}/
As an example, the followings will all result in the same effect without a need for syncing because they are done on a single resource:
PUT /membership/teams/team1/players/player1/
PUT /membership/players/player1/teams/team1/
now if we want to update multiple memberships for one team we could do as follows (with proper validations):
PUT /membership/teams/team1/
{
membership: [
{
teamId: "team1"
playerId: "player1"
},
{
teamId: "team1"
playerId: "player2"
},
...
]
}
/players (is a master resource)
/teams/{id}/players (is a relationship resource, so it react diferent that 1)
/memberships (is a relationship but semantically complicated)
/players/memberships (is a relationship but semantically complicated)
I prefer 2