In this particular example, I think most people would choose option 1. And indeed it feels more natural to me too. Buy why?
Not sure there's going to be a definitive answer to this, but my guess: because password, the entity, has a relatively straight forward representation as a document; HTTP's raison d'etre is document transfer, after all. It's pretty well designed for that.
Additionally, there's a simplicity to password - current state is a simple value type, and consequently there's no confusion about what "modify" should mean. Having only a single method available to mutate the state of the resource doesn't cause any problems because there's only one way you would want to: by completely replacing one representation with another, which is exactly the semantic of the PUT method in HTTP.
Generally speaking, a password change is not idempotent.
Yes, but that's a moot point here; the choice of method in this case is more driven by what should happen if a request is not acknowledged. Do we get to retry? If the message travels through an intermediate, is it allowed to retry the message rather than immediately returning a failure to the client?
(Admittedly, if we are throwing secrets around, we should be using an encrypted channel, which means that the intermediates can't see what's going on)
We aren't sending the request twice because we want the change applied twice, we are sending it twice to ensure that it is delivered at least once.
To ensure that the side effect on the domain doesn't get repeated, use a "conditional put", so that the server can distinguish between two copies of the same request, and two different requests that happen to contain the same representation of the password.
What if we provide a history of password changes as a new end point: /user/{id}/password_changes?
That seems like a reasonable service.
Yup. I would probably lean toward the spelling /user/{id}/password/history
; it feels natural to make the audit log subordinate to the password resource.
And then do we just disallow other http actions to password_changes (PUT, POST, DELETE)? That seems inconsistent... or maybe not?
I don't see a consistency issue. Your domain model is the book of record for password changes; that history is not subject to override by the clients -- the domain reserves that privilege for itself. So the resource is read only. Ta-da?
all that said
We're creating a "password_change". Thus we should be POST to /user/{id}/password_change, or something similar. In this model, the change to the "user" resource is just a side effect of creating a "password_change".
This isn't wrong either; if you twist it around a little bit, you are basically describing AtomPub; you are publishing an entry (the PasswordChanged event) into a feed (the history), and the new "state" of the resource is derived from looking at the history. In other words, RESTful event-sourcing.
It's a bit crooked; the usual idiom is that commands are sent to an aggregate
in the domain model, and the state changes that are caused by the command are published to a stream, which is exposed via an atom feed. In this use case, we turn it around, somewhat, by the fact that the human being, rather than the domain, is the book of record. Maybe.
Jim Webber describes REST application protocols as an analog to a 1950s style office, where you get things done by passing documents into inboxes. You deliver a ChangePasswordRequest to the server, and as a side effect the domain model updates itself, which has the cascading side effect of changing the state of other resources, like the audit log.
That's perhaps overkill for a simple ReplacePassword protocol, but is useful to keep in mind when things get to be more complicated.
With a REST API, is there a convention for clients validate a request without committing any changes?
Not really, not in the way that you mean. REST doesn't care what spelling you are using for your identifiers; in REST, URI are opaque. The client just follows the links that are provided.
So you could send the validate the data to /c255ed19-d6b0-4666-b9cc-abc48d4246ae
and the actual "do it" request to fbb43bdf-0016-4aa1-9f55-28b884238d40
and as far as REST is concerned, those spellings are fine.
So changing the resource identifier to include an intent=validate
parameter in the URI is fine. If you instead decided to distinguish the intent by using a different spelling in the path segments, that's also fine. REST doesn't care; any meaning encoded into URI is done at the discretion of the server and for its own exclusive use.
Of course, if the spelling of the URI is opaque, then that spelling doesn't communicate any useful semantics. There must be an extra layer of indirection somewhere; in REST, that somewhere is a link -- the relation type is the out of band information that the client and server agree upon in advance. In an ideal case, the relation that you need is already standardized; you can check in the link relations registry to see if the information you need is there. If not, you can create a bespoke extention relation type, and assign it a meaning.
<link rel="http://example.org/relations/validateOrder" target="/c255ed19-d6b0-4666-b9cc-abc48d4246ae" />
<link rel="http://example.org/relations/placeOrder" target="/fbb43bdf-0016-4aa1-9f55-28b884238d40" />
You can mint your own identifiers to define these relations, or you can look for matches among the schemas that already exist
<link rel="http://schema.org/CheckAction" target="/c255ed19-d6b0-4666-b9cc-abc48d4246ae" />
<link rel="http://schema.org/OrderAction" target="/fbb43bdf-0016-4aa1-9f55-28b884238d40" />
Best Answer
Different layer. It is important to understand that it is usually not desirable to tightly couple the API layer to the service and/or data layers.
There is no such rule. For starters, REST says exactly nothing about what URIs should look like. Beyond that, it is generally accepted in web API design that singular words are reasonable when only a single instance of the resource exists.
In your shoes, I'd use these endpoints:
A user can GET a document. This will contain a link or a subresource with the most recent revision. To start editing, they POST to draft. This will either return
201 Created
(they are now editing) or409 Conflict
(somebody else is already editing). The user who is editing will PUT to draft. To cancel editing without saving, the user can DELETE draft. To commit the draft, they POST to /revisions. A side effect of this will be to DELETE the draft. Alternately, you can require a manual DELETE of draft by the client to terminate the edit.Now the only problem you have is what happens when the user starts an edit and goes on vacation without cancelling or committing it. :-)