REST: Using a Controller endpoint?
In general REST architectures, the fundamental concept is a Resource. After Resources, the next thing is to develop a Uniform Interface to these resources, which in HTTP land usually means:
- Create is POST
- Read is GET
- Update is PUT (or PATCH for Partial Update)
- Delete is DELETE
In the real world, inevitably some operations won’t map so nicely to resources. This is usually a minority of operations for example reset password. It’s possible to model these as either
- a PUT on /password/
or as
- a Controller endpoint and a POST to /resetpassword
The latter may be considered to be closer to programmatic REST than pure REST, but there are times when clients and customers will want you to be pragmatic. This article gives suggestions regarding when to consider using the Controller option.
Does the action Map to a CRUD?
Several actions in a real world application will not map nicely to a CRUD. For example, Paypal’s cancel billing agreement API is:
POST /v1/payments/billing-agreements/agreement_id/cancel
The cancel action rarely maps nicely to a CRUD for a resource. It could be interpreted as:
- some resource gets be created (A cancel record)
- some resource gets updated (some status column could be getting set to cancelled)
- or some resource gets deleted (a order request gets deleted).
Why should the client have to care about how cancel is handled? Couldn’t it always change? In some case API’s have got around the doesn’t map nicely to a CRUD problem using HTTP tunneling. In the cancelling a billing agreement this would like:
POST /v1/payments/billing-agreements/agreement_id
with body:
{ "operation":"cancel" }
This is considered an anti-pattern and should never be used. Instead a Controller end point should be used.
Resource State or Workflow?
In a REST architecture, every request between Client or Server will usually change a Resource State (write operation) or the Application State (a query or read operation). However, in the real world workflows are inevitable. For example, a reset password flow usually consists of:
- Asking the user for the userId (usually email)
- System checking that email exists on the system
- Sending the user an email with a link to reset the password
- Ensuring the user only has a set amount of time to click the link
- When the user clicks the link they may be asked a bunch of questions
- They will be asked to retype their new password to ensure there’s no typos
When an client action is part of a complex workflow, Resource state and Application state changes may not be easy to model. They may not happen synchronously and they could change based on how the workflow is modelled or when the workflow needs to add an extra step. In such scenarios, consider using a Controller end point.
REST without PUT
In some cases, arguments can be made for avoiding PUT and instead using POST to a different endpoint which signifies intent. For example, to change address instead of invoking a PUT to /address/, the client would invoke a POST to /changeaddress and avoid PUTs altogether. The idea here is to have stricter command / query separation. See https://www.thoughtworks.com/insights/blog/rest-api-design-resource-modeling for more information. In general, it is advised to used PUT / PATCH for update / partial updates and to only use the controller end points when either of the first two reasons apply.
So why there may be subjectivity involved on when to use a controller style endpoint. The above may at least help to you to make a decision. Remember, it should always only be a minority of APIs where you consider this approach. You are outside the conventional Uniform Interface for unique style operations but you want to still make them feel intuitive to clients of the API.
Published on Java Code Geeks with permission by Alex Staveley, partner at our JCG program. See the original article here: REST: Useing a Controller endpoint? Opinions expressed by Java Code Geeks contributors are their own. |
I would argue that CREATE = PUT, UPDATE = PUT they both should be PUTing a complete Representation that is what the the RE in REST stands for. I would argue further that POST should only be used for RPC style calls that have some kind of side effect. Like triggering a job to resize images or something on the server side. I am not a fan of PATCH outside of WebDAV implementations as it tends to get abused. It should be used for large data or binary data if at all.