Friday, May 11, 2018

REST: Using a Controller endpoint?

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:
  • Create is POST 
  • Read is GET 
  • Update is PUT (or PATCH for Partial Update) 
  • Delete is DELETE
There are of course other concepts (statelessness, caching etc) but for this blog post, let's just focus on Resources.

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/ 
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 Create Read Update Delete (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. For 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 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. 

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. 

1 comment: