Chapter 2. Isomorphic JavaScript as a Spectrum

Isomorphic JavaScript is a spectrum (Figure 2-1). On one side of the spectrum, the client and server share minimal bits of view rendering (like Handlebars.js templates); some name, date, or URL formatting code; or some parts of the application logic. At this end of the spectrum we mostly find a shared client and server view layer with shared templates and helper functions (Figure 2-2). These applications require fewer abstractions, since many useful libraries found in popular JavaScript libraries like Underscore.js or Lodash.js can be shared between the client and the server.

chap3-spectrum.png
Figure 2-1. Isomorphic JavaScript as a spectrum

On the other side of this spectrum, the client and server share the entire application (Figure 2-3). This includes sharing the entire view layer, application flows, user access constraints, form validations, routing logic, models, and states. These applications require more abstractions since the client code is executing in the context of the DOM (document object model) and window, whereas the server works in the context of a request/response object.

ijsa 0202
Figure 2-2. Sharing the view layer
ijsa 0203
Figure 2-3. Sharing the entire application
Note

In Part II of this book we will dive into the mechanics of sharing code between the client and server. However, as a form of introduction, this chapter will briefly explore the two ends of the spectrum by looking at various functional application pieces that can be shared between the client and server (we will also point out some of the abstractions required to allow these functional pieces to work isomorphically).

Sharing Views

Single-page applications (SPAs) provide users with a more fluid experience by reducing the total number of full page reloads that they must experience as they navigate from one page to another. Instead, SPAs partially render parts of a page as a result of user interaction. SPAs utilize client-side template engines for taking a template (with simple placeholders) and executing it against a model object to output HTML that can be attached to the DOM. Client-side templates help separate the view markup from the view logic to create more maintainable code. Sharing views isomorphically means sharing both the template and the view logic associated with those templates.

Sharing Templates

In order to achieve faster (perceived) performance and proper search engine indexing, we want to be able to render any view on the server as well as the client. On the client, template rendering is as simple as evaluating a template and attaching the output to a DOM element. On the server, the same template is rendered as a string and returned in the response. The tricky part of isomorphic view rendering is that the client has to pick up wherever the server left off. This is often called the client/server transition; that is, the client should properly transition and not “destroy” the DOM generated by the server after the application is loaded in the browser. The server needs to “dehydrate” the state by sending it to the client, and the client will “rehydrate” (or reanimate) the view and initialize it to the same state it was in on the server.

Example 2-1 illustrates a typical response from the server, which has the rendered markup in the body of the page and the serialized state within a <script> tag. The server puts the serialized state in the rendered view, and the client will deserialize the state and attach it to the prerendered markup.

Example 2-1. Including server-side rendered markup and state
<html>
  <body>
    <div>[[server_side_rendered_markup]]</div>
    <script>window.__state__=[[serialized_state]<]/script>
    ...
  </body>
</html>

Sharing View Logic

Template helpers are objects, like numbers, strings, or hash objects, and are typically easy to share. For sharing formatting like dates, many formatting libraries work both on the server and on the client. Moment.js, for example, can parse, validate, manipulate, and display dates in JavaScript both on the server and on the client. URL formatting, on the other hand, requires prepending the host and port to the path, whereas on the client we can simply use the relative URL.

Sharing Routes

Most modern SPA frameworks support the concept of a router, which tracks the user’s state as she navigates from one view or page to another. In an SPA, routing is the main mechanism needed for handling navigation events, changing the state and view of the page, and updating the browser’s navigation history. In an isomorphic application, we also need a set of routing configurations (i.e., a map of URI patterns to route handlers) that are easily shared between the server and the client. The challenge of sharing routes is found in the route handlers themselves, since they often need access to environment APIs that require accessing URL information, HTTP headers, and cookies. On the server this information is accessed via the request object’s APIs, whereas on the client the browser’s APIs are used instead.

Sharing Models

Models are often referred to as business/domain objects or entities. Models establish an abstraction for data by removing state storage and retrieval from the DOM. In the simplest approach, an isomorphic application initializes the client in the exact same state it was in on the server before being sent back the initial page response. At the extreme end of the isomorphic JavaScript spectrum, sharing the state and specification of the models includes two-way synchronization of state between the server and the client (please see Chapter 4 for a further exploration of this approach).

Summary

Applications can differ in their position on the isomorphic JavaScript spectrum. The amount of code shared between the server and the client can vary, starting from sharing templates, to sharing the application’s entire view layer, all the way up to sharing the majority of the application’s logic. As applications progress along the isomorphic spectrum, more abstractions need to be created. In the next chapter we will discuss the different categories of isomorphic JavaScript and dive deeper into these abstractions.