Showing posts with label rest. Show all posts
Showing posts with label rest. Show all posts

Tuesday, December 31, 2013

5 Levels of REST API Response Statefulness

Stateless : A Best Practice That Isn't Absolute


One of the best practices to follow when designing RESTful APIs is to make the requests stateless.

In simple terms, it means that an API request should contain all the parameters it needs for the server to execute it without depending on the result of a previous request.

In theory, stateless requests brings the following benefits:

  • Scaling: Any server in a cluster can handle the request.
  • Simplified Caching: It's easier to cache the result of a request by using the explicit request state as a cache key.
  • Simplified Troubleshooting: When parts of the request the state aren't explicitly stated, additional investigation is required to figure it out.
  • Reduced Learning Curve: Each API request is self contained. To call the required API, developers don't need to learn or use multiple APIs in sequence.

Following this best practice can be quite challenging since the software requirements sometimes conflict with it. For instance, any API that requires authentication usually relies on one API request to authenticate the user in order to supply valid credentials to subsequent requests.

Since this best practice isn't entirely "black" or "white", I found that defining different levels of statefulness helps when I need to develop new APIs or explain the concept.

Statefulness Levels


  1. Permanent Data
  2. Changing Data
  3. User Specific Data
  4. Session Specific Data
  5. Random Behavior


Level 1: Permanent Data

The request passes explicit state data, the response will always be the same for a specific state.

  • Permanent stateless resources: A data request done on a read-only data warehouse that stores historical information will never change.
  • Permanent resources with a state: A resource may be delivered in different languages, the language selection is explicitly in the request so that the following requests will yield a potentially unique result.
    Example:
    • http://server.com/product/1234&language=french
    • http://server.com/product/1234&language=english


Caching those responses is trivial.

Level 2: Changing Data

The request passes explicit state data,  the response may change over time, even if the state combinations are the same.

Examples:

  • Files: A picture stored at: http://server.com/picture.bmp , that picture might change over time and might eventually be taken offline.
  • Dynamic Data: A business object stored at : http://server.com/product/1234 , that specific object might change over time and might eventually be deleted.


Caching those responses is relatively easy. The challenge lies in defining caching rules to ensure that entries get invalidated in a reasonable time frame.

Level 3: User Specific Data

The response may look different based on the user performing the request.

  • Access Control: Different users might get a different result for the request.
    Example: Given http://server.com/product/1234, if some users don't have access to it, they will receive a "403 Forbidden" response while the other users with access will receive the information on the object.


  • Hidden Data: The response data is based on information hidden from a user.
    Example: The API http://server.com/publicity is used to retrieve the publicity you see on a page, the content of this publicity usually depends on information associated with your user account. This information isn't available to the client performing the request.

  • Personal URL: A URL for which the response is bound to be different between users.
    Example: The URL http://server.com/my-cart. The response data will change based on the user performing the request.

    Note: this can be avoided if the URL could explicitly state the cart identifier in the request such as : http://server.com/cart/5678. This way, API developers can deal with more than one cart. Users might also be able to share their cart with other users.
  • User Preferences : An API that leverages user preferences stored on the server.
    Example: Users store their preferred language on the server. When different users perform the request http://server.com/product/1234 they each get a different result based on their preferred language.

    Note: This can be avoided by tracking the state on the client side.  The client application can read the user preferences and then use them explicitly in every request:
    • Load the current user preferences at http://server.com/user/4567/preferences, returns language=french
    • Reuse the loaded preference in the request http://server.com/product/1234&language=french


Caching those responses is more difficult. On the server side, extra steps are required to determine the correct cache key to use. Edge caching can be impossible since the edge servers won't have all the data they need to return the right response.



Level 4: Session Specific Data

The response data may look different based on the user session performing the request.

  • Session Preferences: The server stores user preferences based on their session.
    Example: A user logs in twice to the same server using two different applications. He configures one application to be French and another in English. The user then performs the request http://server.com/product/1234 on both application and gets a different result.

    Note: this can be avoided by making the client application track the selected language state.


Caching those responses is as difficult as those of level 3.


Level 5: Unpredictable/Random Behavior

The response may look different, even if executed with the same parameters with the same user in the same session.

  • Random Behavior: The behavior isn't predictable.
    Example: An API that returns a random number.




Lower is better

With those levels, the goal is to reach the lowest possible level to gain the benefits of statelessness while satisfying the application requirements.

I hope you'll find them as useful as I did.


Thursday, July 12, 2012

RESTful MVC with a twist


The MCV layers

Smelly MVC

There are multiple approaches to the MVC design pattern, but few of them ever felt right to me.

One of my main gripes with the pattern described on wikipedia is the dependency cycle between the Controller layer and the View layer. Specifically:

  • A controller can send commands to its associated view.
  • A view must communicate to the controller to activate commands.

Dependency cycles are usually a design smell. While something seems wrong, the answer isn't always obvious.

Another problem arises on the controller layer where the code is usually a mix of commands used to manipulate the model and commands to manipulate the view. I can't help but think that those commands don't belong in the same layer.

The V and C switcharoo

The solution I apply to my designs is to switch the View and Controller layer order, leading to Model Controllers Views or the MCV acronym.

The extra 's' aren't there by mistake, but I'll get to it in a moment.

To make it work, I apply a few extra restrictions.

The Controller layer contains no "View" concepts or commands. It is now responsible to modify the model consistently and also to apply security to any changes being done.

The View layers become a bit more thick. They now contain the required logic to build the visual components and interact with their associated control layer.

A new twist comes from the Controller layers that now generates change events consumed by the View layers. This is necessary for model changes that might trigger controller changes. It also adds the benefit that a developer could modify a control layer while the application is running, and the view would keep reacting appropriately.


A RESTful of shmuck


The constraints imposed by REST usually make most developers queasy. How the hell can we make APIs without context ? How can we accomplish every possible change using only PUT, POST and DELETE?

When it comes to monitoring the controller layer, the context restriction hits straight in the teeth. Most controllers need their context to know which user is executing a command. So how do you offer a REST API to monitor a control layer?

The answer : create a control layer instance for each context.

A sample MCV deployment with multiple instances.
Each user gets their instance of a control layer. Just like every view on a system gets a different view layer instance.

This means a RESTful API can be built to fetch each control layer independently. An administrator or a developer could query the control layer of a specific user to ensure that everything is behaving as expected.

Yo dawg, I heard you like MCV...

...so we put a MCV layer in your MCV layer.

If we have a RESTful API on top of our model, our controllers and our views, what stops us from wrapping all of those things together as a single model? Slap development control layers and development view layers on top of it all and use them to monitor, modify and debug all the layers of a running application?

Besides laziness, nothing makes this impossible.

An application MCV wrapped in a development MCV

I applied this in my pet project : The Deduced Framework. It turned out to be quite convenient; allowing the framework to offer a development environment where everything could be changed while the application is running. Whether it's a view, a controller or a model; everything is laid out in plain view. Developing an entire application without ever stopping it to compile is quite a peasant feeling.

It feels like flirting with the future of development tools, where the idea of stopping an application to compile will be as obsolete as manually managing memory allocation or keeping track of the registries on a CPU.

- Steve McDuff