What is the restful way to represent a resource clone operation in the URL?
Since there is no copy or clone method in HTTP, it's really up to you what you want to do. In this case a POST
seems perfectly reasonable, but other standards have taken different approaches:
- WebDAV added a
COPY
method. - Amazon S3 uses
PUT
with no body and a specialx-amz-copy-source
header. They call this aPUT Object - Copy
.
Both of these approaches assume that you know the destination URI. Your example seems to lack a known destination uri, so you pretty much must use POST. You can't use PUT or COPY because your creation operation is not idempotent.
If your service defines POST /resources
as "create a new resource", then why not simply define another way to specify the resource other than as the body of the POST? E.g. POST /resources?source=/resources/10
with an empty body.
Francis' answer is a great one and probably what you're looking for. With that said, it's not technically RESTful since (as he says in the comments) it does rely on the client providing out of band information. Since the question was "what is the restful way" and not "what is a good way/the best way", that got me thinking about whether there is a RESTful solution. And I think what follows is a RESTful solution, although I'm not sure that it's necessarily any better in practice.
Firstly, as you've already identified, GET followed by POST is the simple and obvious RESTful way, but it's not efficient. So we're looking for an optimization, and we shouldn't be too surprised if it feels a little less natural than that solution!
The POST + sourceId solution creates a special URL - one that points not to a resource, but to an instruction to do something. Any time you find yourself creating special URLs like that, it's worth considering whether you can work around the need to do that by simply defining more resources.
We want the ability to copy
resources/10
What if we come up with another resource:
resources/10/copies
...and the definition of this resource is simply "the collection of resources that are copies of resource/10".
With this resource defined, we can now re-state our copy operation in different terms - instead of saying "I want the server to copy resources/10", we can say "I want to add a new thing to the collection of things that are copies of resources/10".
This sounds strange, but it fits naturally into REST semantics. For instance, let's say this resource currently looks like this (I'm going to use a JSON representation here):
[]
We can just update that with a POST or PATCH [1]:
POST resources/copies/10
["resources/11"]
Note that all we're sending to the server is metadata about a collection, so it's very efficient. We can assume that the server now knows where to get the data to copy, since that's part of the definition of this resource. We can also assume that the client knows that this results in a new resource being created at "resources/11" for the same reason.
With this solution, everything is defined clearly as a resource, and everything has one canonical URL, and no out-of-band information is ever required by the client.
Ultimately, is it worth going with this strange-feeling solution just for the sake of being more RESTful? That probably depends on your individual project. But it's always interesting to try and frame the problem differently by creating different resources!
[1] I don't know if makes sense to allow GET on "resources/10/copies". Obviously as soon as either the original resource or a copy of it change, the copy isn't really a copy any more and shouldn't be in this collection. Implementation-wise, I don't see the point in burdening the server with keeping track of that, so I think this should be treated as an update-only resource.
I think POST /resources/{id}
would be a good solution to copy a resource.
Why?
-
POST /resources
is the default REST-Standard to CREATE a new resource -
POST /resources/{id}
should not be possible in most REST apis, because that id already exists - you will never generate a new resource with you (the client) defining the id. The server will define the id.
Also note that you will never copy resource A on resource B. So if you want to copy existing resource with id=10, some answers suggest this kind of thing:
POST /resources?sourceId=10
POST /resources?copyid=10
but this is simpler:
POST /resources/10
which creates a copy of 10 - you have to retrieve 10 from storage, so if you don't find it, you cannot copy it = throw a 404 Not Found.
If it does exist, you create a copy of it.
So using this idea, you can see it does not make sense to do the following, copying some b resource to some a resource:
POST /resources?source=/resources/10
POST /resources-a?source=/resources-b/10
So why not simply use POST /resources/{id}
- It will CREATE a new resource
- The copy parent is defined by the
{id}
- The copy will be only on the same resource
- It's the most REST-like variant
What do you think about this?