I've come across a problem at work where I can't find information on the usual standard or practice for performing CRUD operations in a RESTful web service against a resource whose primary key is a composite of other resource ids. We are using MVC WebApi to create the controllers. For example, we have three tables:
Product: PK=ProductId
Part: PK=PartId
ProductPartAssoc: PK=(ProductId, PartId)
A product can have many parts and a part can be a component of many products. The association table also contains additional information relevant to the association itself than needs to be editable.
We have ProductsController and PartsController classes that handle the usual GET/PUT/POST/DELETE operations using route templates defined as: {controller}/{id}/{action} such that the following IRIs work:
GET,POST /api/Products - returns all products, creates a new product
GET,PUT,DELETE /api/Products/1 - retrieves/updates/deletes product 1
GET,POST /api/Parts - returns all parts, creates a new part
GET,PUT,DELETE /api/Parts/2 - retrieves/updates/deletes part 2
GET /api/Products/1/Parts - get all parts for product 1
GET /api/Parts/2/Products - get all products for which part 2 is a component
Where I am having trouble is in how to define the route template for ProductPartAssoc resources. What should the route template and IRI look like for getting the association data?
Adhering to convention, I would expect something like:
GET,POST /api/ProductPartAssoc - returns all associations, creates an association
GET,PUT,DELETE /api/ProductPartAssoc/[1,2] - retrieves/updates/deletes association between product 1 and part 2
My coworkers find this aesthetically displeasing though and seem to think it would be better to not have a ProductPartAssocController class at all, but rather, add additional methods to the ProductsController to manage the association data:
GET,PUT,DELETE /api/Products/1/Parts/2 - get data for the association between product 1 and part 2 rather than data for part 2 as a member of part 1, which would conventionally be the case based on other examples such as /Book/5/Chapter/3 that I have seen elsewhere.
POST No clue here what they expect the IRI to look like. Unfortunately, they're the decision makers.
At the end of the day, I guess what I am seeking is either validation, or direction that I can point to and say "See, this is what other people do."
What is the typical practice for dealing with resources identified by composite keys?
I too like the aesthetics of /api/Products/1/Parts/2. You could also have multiple routes go to the same action, so you could double up and also offer /api/Parts/2/Products/1 as an alternate URL for the same resource.
As for POST, you already know the composite key. So why not eliminate the need for POST and just use PUT for both creation and updates? POST to a collection resource URL is great if your system generates the primary key, but in cases where you have a composite of already known primary keys, why do you need POST?
That said, I also like the idea of having a separate ProductPartAssocController to contain the actions for these URL's. You would have to do a custom route mapping, but if you're using something like AttributeRouting.NET that is very easy to do.
For example we do this for managing users in roles:
PUT, GET, DELETE /api/users/1/roles/2
PUT, GET, DELETE /api/roles/2/users/1
6 URL's, but only 3 actions, all in the GrantsController (we call the gerund between users and roles a "Grant"). Class ends up looking something like this, using AttributeRouting.NET:
[RoutePrefix("api")]
[Authorize(Roles = RoleName.RoleGrantors)]
public class GrantsController : ApiController
{
[PUT("users/{userId}/roles/{roleId}", ActionPrecedence = 1)]
[PUT("roles/{roleId}/users/{userId}", ActionPrecedence = 2)]
public HttpResponseMessage PutInRole(int userId, int roleId)
{
...
}
[DELETE("users/{userId}/roles/{roleId}", ActionPrecedence = 1)]
[DELETE("roles/{roleId}/users/{userId}", ActionPrecedence = 2)]
public HttpResponseMessage DeleteFromRole(int userId, int roleId)
{
...
}
...etc
}
This seems a fairly intuitive approach to me. Keeping the actions in a separate controller also makes for leaner controllers.
I suggest:
POST /api/PartsProductsAssoc: Create link between part and product. Include part and product ids in POST data.
GET, PUT, DELETE /api/PartsProductsAssoc/<assoc_id>: read/update/delete link with <assoc_id> (not part or product id, yes, this means creating a new column in your PartsProductsAssoc table).
GET /api/PartsProductsAssoc/Parts/<part_id>/Products: get list of products associated with the given part.
GET /api/PartsProductsAssoc/Products/<product_id>/Parts: get list of parts associated with the given product.
Reasons to take this approach:
Single, fully-qualified URI for each link.
Modifying a link modifies a single REST resource.
For more info, see https://www.youtube.com/watch?v=hdSrT4yjS1g at 56:30.
Related
I seek suggestions regarding designing an API endpoint.
I have a table (resource) with id (PK) and some other ids, which are not unique but have not-null constraints.
Now for designing this:
For the PK search /<resourceName>/{id}
Non-PK search
2.1 /<resourceName>/someOtherIdName/{someOtherId} - using path param, distinct for different IDs
2.2 or /<resourceName>?<nameOfId>=<value> - using query param
For 2nd one, which way is better? If I use 2.2, then multiple IDs can be supported but it becomes convoluted, as I have to check the nameOfId. And what about 2.1?
Edit: For example, take transactions to be a resource, and txn_id as primary key, and txn_event_id and txn_activity_id as other IDs. The last two ids can represent a group of related transactions. Does 2.2 suits for the last two IDs?
In case of 2.1, the implementation looks like:
#Path("/transactions")
class TransactionResource {
#Path("/eventid/{event_id}")
public List getTxnWithEventId(#PathParam("event_id") String eventId) {
// do a "event_id" based search
}
#Path("/activityid/{activity_id}")
public List getTxnWithActivityId(#PathParam("activity_id") String txnActivityId) {
// do a "pin" based search
}
}
In case of 2.2, the implementation becomes something like:
#Path("/transactions")
class TransactionResource {
public List getTxnsWithAnotherId(#QueryParam("searchKey") String id,
#QueryParam("searchValue") String value) {
if("event_id".equals(id)) // do a "event_id" based search
else if("activity_id".equals(id)) // do a "activity_id" based search
else return null;
}
}
In my opinion, the 2nd option feels better for searches but why not the former if thats true?
I think it all comes down to the developer's preference. I would not go with either of the options you listed. My approach would be collectionId/resourceId/collectionId/resourceId. So in your case, it would be something like users/1/messages to get all messages of a specific user of users/1/messages/1 to get message with id of 1 for that specific user. That way, you create clearer API endpoints which can be routed more efficiently in your app and can be better documented and managed.
Have a look at how Google's API Design Guide approach this subject for their Gmail resource model:
A collection of users: users/*. Each user has the following resources.
A collection of messages: users/*/messages/*.
A collection of threads: users/*/threads/*.
A collection of labels: users/*/labels/*.
A collection of change history: users/*/history/*.
A resource representing the user profile: users/*/profile.
A resource representing user settings: users/*/settings.
For 2nd one, which way is better?
Either of these is fine for most use cases
/<resourceName>?<nameOfId>=<value>
/<resourceName>/<nameOfId>/<value>
Tomato, tomato.
One reason that you might care about the difference is in the use of relative resolution and dot segments. Dot segments are useful for traversing the hierarchical portion of the URI, which is to say the path segments.
Another reason that you might care is that the query part of a URI has not always been understood to be part of the identifier. Old versions of the HTTP spec described exceptions to the caching rules when the query part was present. In the current standard, it shouldn't make a difference.
If you are struggling with readability of the URI with data encoded into the path segments, there are a number of spelling conventions that may help -- many derive ideas from TBL's work on Matrix URIs. If your clients and servers have access to decent URI Template implementations, then a lot of the work has already been done for you.
I am not sure what your resources are specifically but here are some tips that you can keep in mind while designing RESTful APIs
Identify what the primary resource is.
For example: employees
In your first case, you'd then access employees as
GET /employees. To get all employees.
GET /employees/1. Get a specific employee with ID 1.
Search is specific to your needs. If you need to fetch multiple employees based on IDs, you could do
GET /employees?id=1,2,3,4
Alternately if you find that you will need to search based on more than one parameter, I'd recommend a POST
POST /employees/search
{
id: [1,2,3,4],
department: "computer-science"
}
Consider the following relationship between two resources
College has many Faculties
Faculty belong to a College
Obviously a Faculty is not a first class resource here.
Now I need endpoints for following operations.
Create a new faculty in this college this farm. One possible way to do this in two operations.
POST /faculties/
PUT /college/1/faculties
Remove a faculty from this college. Again two operations
GET /college/1/faculties: List of associated faculties. Each will contain a self url like /faculties/1.
DELETE /college/1/faculties/1: The url looks better but how to expose this url?
Add one or more faculties under that college.
PUT /college/1/faculties that accepts a complete list of faculties of this college.
Delete that particular sector entirely.
DELETE /sectors/1: Looks good but needs to take care of the cache of /faculties/1/sectors.
What would be a better approach in this case? I have read about exposing membership resources, but with that approach, if a college has 10 faculties, it will take 10 seperate http call to get all of those from the memberships.
Moreover, this is just one small part of the full relationship tree. To extend this further, say the system has
Faculties has many Departments
Department has many labs so on.
And besides, In RESTful architecture, the client should never populate the URLs.
Any suggestion?
I wrote a post in the past on how OData implements such aspects (feature "navigation properties"). See this link: https://templth.wordpress.com/2014/12/08/updating-data-links-of-odata-v4-services-with-olingo/.
This other link could also give you some interesting hints since it describes at the end the URLs and corresponding payloads: http://www.asp.net/web-api/overview/odata-support-in-aspnet-web-api/odata-v4/entity-relations-in-odata-v4.
I think that there are two cases you can leverage to minimize the number of request: working with reference or provide content. I mean if the resource detects (based on the content or a custom header) the sent content so it knows if it only needs to handle a reference (attachment only) or a content (creation and attachment).
I would see following possible requests for multiple cardinality (college -> faculties):
POST /faculties/: add a faculty with no attachment to a college
POST /college/1/faculties: attach a faculty to a college and eventually create it if not exist (based on sent content)
DELETE /college/1/faculties/?ref=/faculties/1 to detach a faculty from a college
Something that you could also consider is to put the reference to the college within the faculty (request POST /faculties). So you could attach element during its creation.
Otherwise doing this PUT /college/1/faculties aims to replace the whole representation so all faculties that are attached to a particular college.
You could also use a POST or a PATCH method to minize the number of request. You can have a look at these answers for more details: REST API - Bulk Create or Update in single request and How to Update a REST Resource Collection. Such approach allows you to create elements in one call and then attach them. It allows to gather processing on elements.
Hope I was clear and it helps you,
Thierry
Say I'm trying to model the action of adding a Student to a Group in a RESTful API written in Go with MongoDB.
A Group is modeled like this:
type Group struct {
Section mgo.DBRef
Instructor mgo.DBRef
Students []mgo.DBRef
}
An additional constraint is that the API is implementing HAL+JSON protocol, where resources are represented as links.
I've seen a couple of options (below):
POST /groups/{groupID}/students/{studentID} will add student with studentID to the group. The problem with this approach is that since I'm implementing the HAL+JSON protocol, I don't want the client to have manually pull out the ID and generate this link. All resources will be represented, i.e. /person/123 could be a Student.
PUT /groups/{groupID} while sending the complete array of Students that should belong to the group. This seems like it will introduce a lot of complicated parsing logic.
If there are other options I'd be open to it too.
EDIT: The approach that I'm going with is the following:
* POST /groupmembership/ by sending a JSON with the ID of the student and the ID of the group to add the student to. However, on the backend, I'm not generating a new model, but instead taking the object and programmatically adding the specified student to the specified group.
The question then is how would I remove the Student from the Group? Can I similar send a DELETE request to /groupmembership with
{
"student": 123,
"group": 456
}
to remove student 123 from group 456?
where resources are represented as links
This is not true. Links are possibly operations calls, so they are representing possible resource state transitions.
To add something to a collection, you need a collection resource and you have to decide what you want to store in that collection. In your case this can be 2 things: group-student memberships or students. If this is an 1:n relation, then you can store students and remove students. If this is an n:m relation then you have to store memberships and remove memberships, since you don't want to remove the students from your storage, just the memberships.
You can identify the memberships 2 ways:
you can use the ids of the participants: /groups/1/memberships/student:1 or /students/1/memberships/group:1
you can add a unique id to each membership: /memberships/1234
notes:
The URI structure matters only from a human perspective. The REST client will check the link relations and not the URI structure.
The resources are different from the entities in your database. Only by simple CRUD application represent them the same thing. So REST has nothing to do with your database structure.
First of all, there's no correct REST endpoint. URL semantics are irrelevant to REST. All that matters is that URLs are obtained from hypertext and not from out-of-band information, and seems like you got that part right, since you're using HAL. So, the correct REST endpoint is whatever link your server gives to the clients in order to add the item.
As long as an option isn't incorrect from an HTTP standpoint, I'd say to stick with whatever is more consistent with the REST of your API.
The option to POST /groups/{groupID}/students/{studentID} in order to create a new student in that location is incorrect, since a POST is submitting the payload to be processed by the targeted resource, and in this case it doesn't exist yet. A common pattern is to use POST /groups/{groupID}/students, where the collection acts as a facory for new elements, with the creation parameters in the payload, and returning the created student URL in the Location header, with 201 HTTP status code.
Lets assume I have an object that I expose as a REST resource in my application. This object has many fields and contains many other objects including associated collections. Something like this, but think MUCH bigger:
Customer
List<Order> orders
List<Address> shippingAddresses;
// other fields for name, etc.
Order
List<Product> products
// fields for total, tax, shipping, etc.
Product
// fields for name, UPC, description, etc.
I expose the customer in my api as /customer/{id}
Some of my clients will want all of the details for every product in each order. If I follow HATEOAS I could supply a link to get the product details. That would lead to n+1 calls to the service to populate the products within the orders for the customer. On the other hand, if I always populate it then many clients receive a bunch of information they don't need and I do a ton of database lookups that aren't needful.
How do I allow for a customer representation of my resource based on the needs of the client?
I see a few options.
Use Jackson's JsonView annotation to specify in advance what is used. The caller asks for a view appropriate to them. i.e. /customer/{id}?view=withProducts. This would require me to specify all available views at compile time and would not be all that flexible.
Allow the caller to ask for certain fields to be populated in the request, i.e. /customer/{id}?fields=orders,firstName,lastName. This would require me to have some handler that could parse the fields parameter and probably use reflection to populate stuff. Sounds super messy to me. The what do you do about sub-resources. Could I do fields=orders.products.upc and join into the collection that way? Sounds like I'm trying to write hibernate on top of REST or something.
Follow HATEOAS and require the client to make a million HTTP calls in order to populate what they need. This would work great for those that don't want to populate the item most of the time, but gets expensive for someone that is attempting to show a summary of order details or something like that.
Have separate resources for each view...
Other?
I would do something like this:
/customers/{id}/orders/?include=entities
Which is a kind of a more specific variation of your option 1.
You would also have the following options:
Specific order from a specific customer without list of products:
/customers/{id}/orders/{id}
Just the orders of a customer without products:
/customers/{id}/orders/
I tend to avoid singular resources, because most of the time or eventually someone always wants a list of things.
Option 2 (client specifies fields) is a filtering approach, and acts more like a query interface than a GETable resource. Your filter could be more expressive if you accept a partial template in a POST request that your service will populate. But that's complicated.
I'm willing to bet all you need is 2 simple representations of any complex entity. That should handle 99.9% of the cases in your domain. Given that, make a few more URIs, one for each "view" of things.
To handle the 0.1% case (for example, when you need the Products collection fully populated), provide query interfaces for the nested entities that allow you to filter. You can even provide hypermedia links to retrieve these collections as part of the simplified representations above.
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.