The REST architecture has become increasingly recognized for its value in creating scalable, loosely coupled systems. REST is presented as a network interaction architectural style, not a programming methodology. However, the principles of REST can actually be very meaningfully and beneficially applied in the programming realm. We will look at how the resource oriented approach of REST can be applied as principles for programming language usage and design. The motivation for looking at REST should be clear. Little in history has been as ridiculously successful and scalable as the web, and REST is a retrospective look at the principles that were employed in designing the core technologies of the web, particularly HTTP. Applying such proven principles to our application design will certainly be beneficial.

Roy Fielding‘s REST architecture is broken down into seven constraints (and the four sub-constraints of the uniform interface). The individual concepts here are certainly not new, but collectively looking at these concepts as resource oriented programming may provide an interesting new perspective. I will also look at how these principles are exemplified in Persevere 2.0 in its object store framework, Perstore, and its web stack Pintura.

Client-Server

Of all the REST principles the first principle is most targeted at the network paradigm, and is the least compelling for advising programming. However, it does suggest the importance of an asymmetric relationship between modules with separation of concerns. When modules interact, one module should stick to the role of providing a service (acting as the server), and one should act as the consumer of the service. The roles and concerns of the two modules thus stay cleanly separated.

Stateless

If you follow computer science research, you are probably aware that one of the predominant focuses of modern programming languages is seeking to minimize the hazards of stateful imperative programming. Many developers are moving towards functional languages, or at least functional elements of languages, which perform computations on inputs, yielding outputs, rather than being defined as a series of state changes. Concurrency, security, scalability, and many other programming concepts become drastically simpler when mutating state is removed, and applications become much easier to develop. This directly corresponds with the stateless principle of REST. Maintaining transitive state information puts an enormous burden on modules in terms of scalability and complexity. Minimizing or eliminating state and using functional paradigms yields much better code.

When we consider the stateless constraint of REST, there is an important distinction to be made between “state” and “data” or “resources”. Since REST is clearly resource oriented, data/resources are obviously not something that we are trying to eliminate. When we are advising against state, we are advising against the use of transitive state information as the primary programming technique for developing and implementing application flow and logic. The resources and data to which an application provides a user interface are still mutable structures, but this interaction is carefully handled through the uniform interface (coming later).

Cache

Another key concept of a resource oriented approach is that it provides visibility for interaction. REST’s interaction explicitly defines cacheability and idempotence of actions. Certain operations are known to be safe and cacheable. In HTTP, GET requests fit this criteria. In standard object oriented programming conventions, a getter is safe and cacheable as well. If a uniform interface has been defined, one can define one or more retrieval function that are safely cacheable. The uniform interface must explicitly disclose which operations have side-effects. This can have very significant implications for creating well-optimized code. We can re-utilize local copies of data without re-calling retrieval functions if the functions are known to be cacheable. In the realm of purely opaque functions we are afforded no such opportunities (who knows if a function might have side-effects).

Getters should be safe and cacheable (and idempotent), and setters should be idempotent. Getters and setters provide an extremely powerful concept within programming. Distilling everything down to opaque calls hides important semantics from users that can distinctly alter their expectations and use of modules, but getters and setters expose these expectations in a way that benefits users. Developers can expect that object.foo can be evaluated multiple times with the same result unless the property has been altered. Setting object.foo is an idempotent operation (i.e., it can be safely repeated) and can be expected to alter the value returned by object.foo (if successful).

Uniform Interface

The most central concept of REST is the uniform interface, often characterized by HTTP’s standard methods: GET, PUT, POST, DELETE, etc. as the means for navigation and interaction. These methods have clear semantics. GET is safe, PUT, POST, DELETE have side-effects. GET, PUT, and DELETE are idempotent, POST is not. PUT is supposed to update a resource in a way that predictably alters the expected response to a GET.

These methods have analogies in object programming with getters and setters as we have discussed; however a uniform interface can be more than simply getters and setters. The uniform interface does not act solely on the internal state of individual objects, but can affect the collection of some type of objects, providing means for creating and deleting objects, querying for objects, retrieving by unique identifier, and even locking and unlocking the objects.

Perhaps one excellent example of the uniform interface principle in JavaScript is Dojo‘s Data API. While it doesn’t utilize the traditional HTTP-style set of verbs, this API does follow the uniform interface principle and provides a single generic uniform interface through which all widgets can access data, and the data source can implement this interface to provide generic access to various different storage systems or file formats.

The W3C is working on a compelling new API called the indexed database API that could provide a more broadly accepted generic uniform interface for JavaScript, and even bears quite a bit of resemblance to the traditional HTTP verbiage for verbs, with get(), put(), and delete() methods. This API is used by Persevere 2.0 in it’s object store framework, Perstore, and further leveraged by its web stack Pintura. With this interface, various different storage systems, especially NoSQL servers, can very easily be modeled and leveraged at different layers within the Pintura/Perstore ecosystem.

The uniform interface is further broken down into four important sub-constraints.

Identification of Resources

A fundamental concept of REST is that resources must have a unique identifier which can be used to retrieve that resource (a URL). The identifier must be unique and carry sufficient information to locate the resource. If the identifier is only unique within a given subsystem, additional information must be added to the identifier if it is referenced outside the subsystem. This is in fact how URLs work. The paths are unique to a server, but the path is combined with the server host name to make them unique across the entire Internet. When applied to programming, resources are objects that are identifiable. Within an application runtime, it is often sufficient to simply use the server unique identifier, or even within the scope of certain modules use an identifier specific to a given table or data store.

Within Perstore, all resource objects must have an identifier property or getId() function that returns the identity of the object. Resource objects can be retrieved using the store.get(id). These mechanisms guarantee a way to discover a resource’s identity and retrieve the resource by the identity.

Manipulation of Resources through Representations

REST defines a distinction between resources and representations. Resources are not necessarily simple files. Resources may represent abstract concepts such people, computers, events, and more, and may be stored and accessed through various means such as database records. In the scope of network communication, representations provide a means for describing these resources using byte-level serialization. REST recognizes that different consumers may need or prefer different types of representations, and supporting multiple representations for a given resource can be highly valuable.

In intra-machine programming, of course modules can interact with higher level constructs than byte-level data, and can use objects, numbers, strings, and language constructs. However, there still may be multiple ways of representing an abstract resource. These can be particularly important if different data consumers expect data to be structured in different ways.

One of the ways that the abstraction between a representation and a resource can be achieved is with facets. I have previously discussed how facets are a powerful tool for securely controlling access to resources in the object capability model. Facets act as a wrapper for an object store, and can not only attenuate access, but can provide alternate “views” of data/resources. For example, facets can easily be used for localization. You could create one facet for a set of resources that provides methods and properties for English speakers, and then another set of methods and properties for French speakers. The facet could include any necessary logic for pulling data from different data sources based on locale. This use of facets is a clear demonstration of separation of representation/view and resource.

Self-descriptive Messages

Self-descriptive messages describe how we view and interact with a resource. There are a couple aspects of this principle. First, we should be able to easily introspect a representation of a resource to comprehend the structure and content, and use this information to intelligently update the object. Objects with properties (or hash/map style name-value pairs, which is the same thing in JavaScript) are well suited for this since the introspection clearly implies how the object can be updated. Self-descriptive objects are easier to interact with because generic tools can interact with them. The API for interacting with the object is evident from the object itself.

Self-descriptive messages also means that in addition to the raw representation of a resource, we should also be able to view additional metadata that describes extra information about the resource such as cacheability, attribution information, etc. Here, Pintura defines a getMetadata() function for resource objects that provides access to metadata information for resources and their representations. This makes metadata readily available for processing conditional requests (based on modification times or etags) and defining caching levels.

Hypermedia as the Engine of Application State

The ability to navigate resources through hyperlinks/hypermedia is the hallmark of REST. This is easy to relate to object oriented programming where property values can be references to other objects or getters can return other objects, making it easy to navigate through data structures. We frequently utilize object mapping to maintain these types of object references from relationships indicated by underlying data sources.

Perstore leverages JSON Schema’s hyperlink description capabilities to define links based on data. The JSON Schema implementation used by Perstore provides a getLink() function to the target of a resource link as defined in the schema definition. For example, we can define a model in Perstore:

var Model = require("model").Model;
Task = Model("Task", dataStore, {
  links: [
    {rel: "project", href: "/Project/{projectId}"}
  ]
});

And then navigate through the data:

var getLink = require("json-schema/links").getLink,
    someTask = Task.get("some task id");
someTask.projectId -> id of a the project it is part of
someTask.get("project") -> retrieves the target project through the defined link

getLink(“project”, someTask, Task) -> “/Project/project-id”

Layered

REST advises layering as the technique for composing functionality and scalability from multiple agents. In programming, layering can also be used, and is best described as wrapping or composition. With programmatic layering, one object “wraps” another source object and handles all messages before sending them on to the source object. The wrapper can then add functionality to the source object and still expose the same interface to object users.

The programmatic layering principle is also seen in middleware. The HTTP interface of middleware-centric web applications consists of layers of middleware, where each middleware acts as an HTTP intermediary, adding functionality along the way. Pintura, the HTTP interface for Persevere 2.0, is completely driven by this middleware-centric approach. Pintura is basically a set of middleware providing authorization, CSRF protection, content negotiation, and more.

Perstore makes extensive use of the layering approach as well. Perstore uses a single REST-style uniform interface object store API (with get, put, delete from the W3C Indexed DB API), that is used throughout the framework. Low level data storage adapters (for MongoDB, JSON files, CouchDB, SQL, etc), can implement this API, and then store wrappers can be layered on top to add additional functionality such as caching, subscription/notification, aggregation, replication, and adaptable-indexing. For example, the we can take a store and add notification and caching to it:

var Notifying = require("store/notifying").Notifying,
    Cache = require("store/cache").Cache,
    Memory = require("store/memory").Memory,
var enhancedStore = Notifying( // add notifications
   Cache( // add caching
      dataStore, // our original source store
      Memory())); // cache in memory

Perstore’s models and facets are layers themselves, also implementing the same interface, and provide schema validation and access control. These various layers are effectively an object store form of middleware.

Code on Demand

The Code on Demand principle provides a pathway to extensibility for applications. A traditional basic resource may simply be data, but this principle advises that resources may contain code (or be code entirely), providing language level flexibility for richer content than is possible for simple static data structures. As the LISP mantra goes, “code is data and data is code”. JavaScript can easily be used in homoiconic ways, supporting this paradigm well. The object literal syntax of JavaScript (the proper superset of JSON), is well adapted to include functions holding programmatic logic. By supporting storage of code/functions in databases, one can easily define individual resource-specific logic, and resource consumers can easily interface to resource functions as means for allowing resource-specific extensibility in applications.

Summary

These seven principles (and the four sub-principles of uniform interface) collectively integrate to form the powerful architectural style known as REST. This important network architecture can be applied as a programming style in resource oriented programming, and Persevere 2.0 provides a great framework for building applications with this methodology.