Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to represent\maintain a master detail relationship in a RESTful way?

Behind our ASP.net Web API we have a code first entity framework model. Consider the following (simplified) entities:

public class Form
{
    public long Id { get; set; }

    public string Identifier { get; set; }

    public virtual FormGroup Group { get; set; }
}

public class FormGroup
{
    public long Id { get; set; }

    public string Identifier { get; set; }

    public virtual ICollection<Form> Forms { get; set; }
}

As you can see there is a 1-Many relationship between FormGroups and Forms. My question is, how should one design a RESTful API that allows us to change which Group a Form belongs to?

I have so far come up with two potential approaches, with pros and cons for each:

Represent the relationship as a link structure:

{
    "Id" : 1,
    "Identifier" : "Form1",
    "link" : {
        "rel": "http://myapi/res/formgroup",
        "href": "http://myapi/formgroups/1"
    }
}

Pros:

  • Includes a meaningful link to the formgroup representation

Cons:

  • There is no particular standard for such links
  • Server side logic becomes more complicated

Use PUT to link resources together

data: {
    "Id" : 1,
    "Identifier" : "Form1"
}

PUT data to http://myapi/formgroups/1/forms

Pros:

  • No extraneous properties on representations

Cons:

  • Resources can be identified by multiple URIs i.e /forms/1 and also /formgroups/1/forms/1
  • Sending any data other than that required to form a key is either extraneous, or confusing

I am not particularly happy with either of these approaches as they each seem to have more drawbacks than positives.

I know that a 'proper' solution would be to implement DTOs and seperate my entity model from my resource representation, which would allow me to add say a FormGroupId which I can set.

Like all good programmers however I am curious to see how this is intended to work in a RESTful API. I would also like a solution that I can apply generically to any navigation properties in my model.

Answers on a postcard... Are either of these approaches valid or is there another way of doing things that I have missed?

Pete

like image 345
beyond-code Avatar asked Feb 06 '13 19:02

beyond-code


People also ask

What is link relation in REST API?

A link relation is a descriptive attribute attached to a hyperlink in order to define the type of the link, or the relationship between the source and destination resources. The attribute can be used by automated systems, or can be presented to a user in a different way.

What do master detail relationships involve?

Master-detail relationships allow data from three objects to be joined in one report: the master object, the detail object, plus one other lookup object.


2 Answers

Building your API based on a data model will only restrict your overall design. I tend to work back from my representations.

With complete control over your representations you can express relationships however you wish. For example, you may decide to include a representation of your FormGroup within your Form representation:

GET /api/forms/1

    {
      "Id" : 1,
      "Name" : "Form 1",
      "FormGroup" : {
        "Id" : 1,
        "Name" : "Form Group 1"
      }
    }

Or include the Forms in the FormGroup representation:

GET /api/formgroups/1

    {
      "Id" : 1,
      "Name" : "Form Group 1",
      "Forms" : [
        {
          "Id" : 1,
          "Name" : "Form 1"
        },
        {
          "Id" : 2,
          "Name" : "Form 2"
        }
      ]
    }

In fact you could do both. Just ensure that each resource only has a single URI identifier.

If you really want to embrace REST and include hypermedia links in your API responses I recommend you check out Hypertext Application Lanaguage (HAL). HAL provides a convention for expressing related links between resources. For example:

{
  "_links" : {
    "self" : { "href" : "/api/forms/1" },
    "related" : { "href" : "/api/formgroups/1" }
  }
  "Id" : 1,
  "Name" : "Form 1",
  "FormGroupId" : 1
}

Hopefully you can see that how you represent your resources does not determine how you manage the relationships between them.

How you do this should largely depend on what is logical in your scenario and can often be helped by deciding who owns the relationship.

Take the typical Blog Post .* Comments example. Comments make little sense by themselves. If I was to design an API for managing blog comments I'd likely do it like so:

POST   /posts/1/comments  - Add a comment to post 1
DELETE /posts/1/comments/1 - Remove comment 1 from post 1

Note that the comment does have identity, but only within the context of the blog post.

In your case (based on our discussions on twitter) you said that Forms do have identity outside of a FormGroup. You also said that you can have orphan forms - those that do not belong to a group.

With this in mind, it would make sense that you manage the FormGroup relationship via the Form resource. The simplest way would be to just set/update a FormGroupId property when you perform a POST or PUT to the forms resource. If (again this depends on your scenario), you require the ability to change the FormGroup without updating any other properties on a Form resource you may wish to send a PATCH or create a "sub-resource" for managing the relationship:

POST /api/forms/1/group -- set the form group for forms/1
{
  "formGroupId" : 10
}

DELETE /api/forms/1/group -- unlink forms/1 from its group

Unfortunately there is no "right way" to represent relationships between your resources. My advice is to just go with what feels the most natural.

like image 113
Ben Foster Avatar answered Nov 13 '22 06:11

Ben Foster


You are right. Currently the world of resources on ASP.NET Web API is mixed one where you define your routes somewhere while you have to managed resource relationship and link somewhere else. You can do hierarchical routing by defining each route on its own which has maintenance and performance implications.

I am thinking of bringing these together. Watch the space :)


Now in terms of response to your question, I would go with this:

/api/formgroups/1
/api/formgroups/1/forms/123

Maintaining links and setting up routes will still be more or less ad-hoc - for now.

like image 22
Aliostad Avatar answered Nov 13 '22 06:11

Aliostad