RESTful 1-N optional relationships

Question!

I trying to learn how to write RESTful apps in Java using Jersey and Hibernate, and I'm struggling to understand how to handle parent/child type relationships when POSTing data to a Resource. I'm using JSON to exchange data, but I don't think that's particularly relevant to my problem.

The example I'm working with models the relationship between employees and teams. An employee may, or may not, be a member of one team:

GET /team/ - returns a list of teams
POST /team/ - creates a new team
GET /team/1 - returns a list of employees in the team with the ID 1

GET /employee/ - returns a list of employees
POST /employee/ - creates a new employee
GET /employee/1 - returns details about the employee with the ID 1

Behind this I have some Hibernate annotated POJOs: one for team, and one for employee, with a 1-N relationship between the two (remember that an Employee may not be a member of a team!). The same POJOs are also annotated as @XmlRootElements so that JAXB will allow me to pass them to/from the client as JSON.

The properties for the two entities look like this:

Team
    Long id;
    String name;
    String desc;
    List<Employee> employees;

Employee
    Long id;
    Team team;
    String name;
    String phone;
    String email;

All good so far. But I'm struggling to understand how to make an employee a member of a team at creation-time by just passing in a Team ID, rather than passing in a nested team object in my JSON object.

For example, I'd like to be able to call POST /employee/ with a JSON that looks like this:

{
    "team_id":"1",
    "name":"Fred Bloggs",
    "phone":"1234567890",
    "email":"[email protected]"
}

But, instead, I have to pass in something like this:

{
    "team":{
        "id":"1",
        }
    "name":"Fred Bloggs",
    "phone":"1234567890",
    "email":"[email protected]"
}

So, my question is, how do others handle creating relationships in JSON/REST without passing around whole object graphs?

Sorry this is such a sketchy question, but as I say, I'm just starting out, and terminology is a problem for me at this stage!

By : blacksky


Answers

REST offers the possibility to use URLs as references, too, which I find really cool. So it would look like this:

{
    "team":"http://myapp.com/team/1",
    "name":"Fred Bloggs",
    "phone":"1234567890",
    "email":"[email protected]"
}

You can avoid passing nested objects, too by just supplying a

{
    "team":"1",
    "name":"Fred Bloggs",
    "phone":"1234567890",
    "email":"[email protected]"
}

In that case your converter must be smart enough to figure that if the value of the team key is a string (or integer, whatever works) and not another JSON object it should be interpreted as an id.

By : Daff


There are multiple ways to approach a solution for this problem. Its a class hyperlinking problem in the domain of RESTful Web Services. Since this has to do with Jersey the first thing I would recommend is to avoid JAXB all together since JAXB (in context of XML or JSON) ain't HATEOAS.

After dwindling a lot with Jersey and HATEOAS I have come to the opinion that the best representations for a RESTful WS is Atom Syndication Format coupled with JSON. For your example of Team and Employee I would take the following approach.

GET /team/ - returns a paginated Atom Syndication Feed list of teams
POST /team/ - creates a new team receiving a JSON representation
GET /team/1 - returns a paginated Atom Syndication Feed list of employees in the 
              team with the ID 1 with an Link to team's JSON representation too.

GET /employee/ - returns a paginated Atom Syndication Feed list of employees
POST /employee/ - creates a new employee using JSON
GET /employee/1 - returns details about the employee with the ID 1 in JSON

Till here I haven't change much just specifying some representation details. The interesting part is adding/removing an employee from a team. For this I would add resources with pattern

@Path("/team/{id}/employees)
class TeamEmployees {

  @Path("/{posId}")
  @GET
  @Produces(MediaType.APPLICATION_JSON)
  public Employee get(@PathParam("posId") int positonId) {}

  @Path("/{posId}")
  @DELETE
  public Employee remove(@PathParam("posId") int positonId) {}

  @POST
  @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
  //empUri sample is /employee/{id} server knows how to parse it
  public Employee add(@FormParam("employeeUri") String empUri) {}

}

Now what is position id, one approach - it is a unique number across all teams, i.e. primary key in a table which will have position_id, team_id, emp_id as tuple. Ensuring that there will never be 2 position_id same for 2 teams. This way the whole scenario could be turned to be HATEOAS.

In addition to be able to export URIs in JSON representation to a DTO, the approach that I take is, I have DTO representing the data model for its persistent storage and I have a representation model representing hyperlinked (de)serializable version of DTO where I store string value as hyperlink. I look at representation model as API and DTO as the SPI to the RESTful WS Data Model.

By : imyousuf


If your framework forces your representation to include strange constructs like { "id":"1" } then I'd say it's time to switch framework!

More importantly, instead of worrying about adding a sub-JSONObject to your code, I would worry that the term "1" is indeed not really a hyperlink. Read up on the hypermedia constraint, or HATEOAS if you want.

What you want to pass in your POST is this:

{
    "team_href" : "/teams/1",
    "name":"can'tbebothered"
}

so when the server sees this, it links the newly created employee with team #1 merely because it recognises the (relative) URI.

By : mogsie


This video can help you solving your question :)
By: admin