Why not both?
Which is to say, yes, there are trade offs to consider, but if the marginal cost of implementing a second option is small, you can offer to your clients the ability to select which representation they prefer, so that they can choose their own trade offs (of course, there's some complexity penalty to be paid by offering a choice, rather than solving "the" problem for the clients).
The major con I see is that you've built an endpoint tightly coupled to this particular view within this particular application.
Not quite the right language, from a REST perspective. It's not the endpoint that is coupled to the application, but the media type of the representation.
Of course, worrying about media types tends to fall by the wayside when we are implementing both the client and the server, and their release cycles are coupled.
The pros of 2. are that we are using nothing but generic resource endpoints, which could be re-used by many different views and applications.
That thought is incomplete - you can not only reuse the endpoints, but you can re-used the representations themselves... ie: caching. If the client can pull the data it needs out of its own cache, then it doesn't need to round trip at all. Failing that, an intermediate cache may already have a copy of the data, shortening the round trip. The "server" that the client is talking to might be a cache farm in front of your app, keeping the workload low while being able to scale out.
In REST, you want to make sure that your designs take advantage of the uniform interface.
So one of the things you should be thinking about is the cache lifetime of your resources; how long are representations valid? Are other views and applications going to be able to take advantage of that?
Should the fact that this API is an internal company API (and almost certain to remain so), rather than a public facing one, influence my decision?
That's likely to put limits on the volume of traffic you'll need to support. Also, if the clients are all going to be centrally located, then round trip time falls away as a concern as well.
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.
Best Answer
Only if you need it. Do you?
It could be a bad idea only if you don't need it.
Take into account that REST API are contracts. Once in production, changing these contracts can be really challenging. Overall if we are enforced to support backward compatibility. Having to maintain YAGN-ish code reverts against our interest, so it's preferable to release only functional code.
How do we know if we need it?
Read carefuly the requirements because, this's rather an implicit need.
For example:
Exams must be set only with available questions. Let's say that exams are set picking questions from a repository. If this is the case, the API should provide us with this repository. The repository -essentially- is the collection
/api/questions
.Certain roles are allowed to add new questions for further usage in different exams. Here, we are assuming several things: 1) questions don't necessarily belong an exam, 2) questions can be managed independently, 3) questions can belong to different exams. Any of these cases will lead you to provide the API with a repository of questions.
Exam's questions are composed by the reference to its formulation and its valid answer, the committed answer, evaluator's side notes, the score, etc... Here, we are assuming that both URIs are not identifying the same resource. Questions within the scope/context of an exam are different than questions scoped from the repository.
Weight the pros & cons of that "slightly more intuitive". If the benefits of the so little improvement outweigh the costs of YAGNI (usually they don't), then go for it. Otherwise, just forget it.
Readings you might be interested