In REST architectures, the fundamental concept is a Resource. A Resource represents anything that’s important enough to be referenced as a
thing in itself. For example, a Shopping Cart, a Book or a Car. The next fundamental concept is the Uniform Interface for accessing and manipulating the Resources. In HTTP land usually means:
In the real world, many things map nicely to Resources. However, inevitably somethings 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
- Create is POST
- Read is GET
- Update is PUT (or PATCH for Partial Update)
- Delete is DELETE
In the real world, many things map nicely to Resources. However, inevitably somethings 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/
- a Controller endpoint and a POST to /resetpassword
Does the action Map to a CRUD?
Several actions in a real world application will not map nicely to a Create Read Update Delete (CRUD). For example, Paypal's cancel billing agreement API is:
POST /v1/payments/billing-agreements/agreement_id/cancelThe 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).
POST /v1/payments/billing-agreements/agreement_idwith 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
REST without PUT
In some situations, 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. One example where this approach is useful is when handling asynchronous operations and you are trying to make clear atomic consistent operation. So for example, if changing address takes a long time and you would rather return a 202, with a location field for the client to poll, if you use the /changeaddress you can then leave /address endpoints as those that are only atomically consistent.
So, any PUT or POST to address, means if you were to immediately do a GET you would get the consistent view of the Resource. This approach is also useful if you want to model the Business event rather than the actual resource that is changing. So for example, suppose 6 or 7 things need to take place when a Bank account has been closed for a Business process perspective. All on the back end in the same thread / transaction. Again, here POST to controller endpoint such as /accountclosed makes more sense then /DELETE to /account.
See this article for more info.
So, any PUT or POST to address, means if you were to immediately do a GET you would get the consistent view of the Resource. This approach is also useful if you want to model the Business event rather than the actual resource that is changing. So for example, suppose 6 or 7 things need to take place when a Bank account has been closed for a Business process perspective. All on the back end in the same thread / transaction. Again, here POST to controller endpoint such as /accountclosed makes more sense then /DELETE to /account.
See this article for more info.
Summary
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.
Skip REST go back to RPC (JSON-RPC of course)
ReplyDelete