REST API design for cloning a resource

dev'd picture dev'd · Jun 12, 2015 · Viewed 9.4k times · Source

I am writing a YAML document using swagger to design a RESTful API method for cloning a resource. I have a few options and don't know which would be best. Please can someone advise?

Options:

  1. Relinquishing the responsibility of cloning the resource object to the consumer (where the consumer assigns values to properties on a new object and then creates a new object), the process would need to consist of two requests to the API: a GET against a resource for the source object and then a POST to that resource for creating the new one. This feels like the consumer has too much responsibility.
  2. Using the WebDAV HTTP extensions which provides a COPY method (see here). It would appear that this is exactly what I would like for cloning. However, I would like to stick to the standard methods as much as possible
  3. POSTing to /{resource}?resourceIdToClone={id} where resourceIdToClone is an optional parameter. This would conflict with an API path that I already have for creating the resource, where I add a schema to the POST body. It would mean using a POST to /{resource}/ for creating and cloning, and that would violate SRP.
  4. Adding a new resource called 'CloneableResource' and performing a POST to /CloneableResource/{resource_type}/{resource_source_id}. For the example of cloning a sheep, you'd make a POST to /CloneableResource/Sheep/10. This way, it would be possible to stick to using the standard HTTP methods, there'd be no conflict with any other resource paths (or SRP violation). However, I would be adding a new and potentially superfluous type to the domain. I also can't think of a scenario when a consumer would want to perform anything other than a POST to this resource, so it seems like a code smell to me.
  5. A GET against /resource/{id}?method=clone. One of the advantages here is that no additional resource is required and it may be determined by a simple optional querystring parameter. I'm aware that one of the risks here is that it can be dangerous to provide post or delete capabilities using a GET method if the URL is in a web page as it may be crawled by a search engine.

Thanks for any help!

Answer

Jason Desrosiers picture Jason Desrosiers · Jun 12, 2015

Most of these options are perfectly good choices. A lot of it just your style choice in the end. Here are my comments on each of your options.

  1. Relinquishing the responsibility of cloning the resource object to the consumer
    In general I don't really have a problem with this solution. This option is very straight forward for a user to understand and implement. It might be better than coming up with some proprietary cloning functionality that your users have to learn how to use.

  2. Using the WebDAV HTTP extensions which provides a COPY method
    I like to stick to the standard methods as well. I would not use COPY, but I wouldn't appalled if you did.

  3. POSTing to /{resource}?resourceIdToClone={id}
    This is a perfectly good solution. From a REST standpoint, you don't really have a conflict with the rest of your API. The URI with a query parameter identifies a different resource than the URI without the query parameter. Query parameters are a URI feature for identifying resources that you can not be referenced hierarchically. However, it might be difficult to separate these in your code because of the way most REST frameworks work. You could do something similar to this except with a hierarchical URI such as /{resource}/clone. You could POST to this URI and pass the resource_source_id in the body.

  4. Adding a new resource called 'CloneableResource' and performing a POST to /CloneableResource/{resource_type}/{resource_source_id}
    There is nothing wrong with this approach from a REST standpoint, but I think adding a new type is both unnecessary and clutters the API. However, I disagree with your intuition there could be problem with having a resource that has only a POST operation. It happens. In the real world, not everything fits nicely into GET, PUT, or DELETE.

  5. A GET against /resource/{id}?method=clone
    This is the only option of the 5 that I can not condone. It seems from your description that you already understand why this is a bad idea, so I'm not sure why you are considering it. However, all you have to do to make this a good solution is to change GET to POST. It then becomes very similar to the #3 solution. The URI could also be hierarchical instead of using a query parameter. POST /resource/{id}/clone would work just as well.

I hope this was helpful. Good luck with your decision.