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:
Cons:
Use PUT to link resources together
data: {
"Id" : 1,
"Identifier" : "Form1"
}
PUT data to http://myapi/formgroups/1/forms
Pros:
Cons:
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
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.
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.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With