An Introduction to Isomorphic Web Application Architecture

This article has been excerpted from Isomorphic Development with JavaScript

Isomorphic Web App Overview

To understand what an isomorphic web app is, we’re going to use an example web app called All Things Westies. On this site, you can find places to adopt a Westie (West Highland White Terrier, a small white breed of dog). You can also get information about Westies, purchase dog supplies and buy products featuring Westies (socks, mugs, shirts, etc.). Because this is an ecommerce app, we care about having a good Search Engine Optimization (SEO) presence and we want our customers to have a great experience using the app.

How it works

Look at Figure 1, which is a wireframe for the All Things Westies app. There’s a standard header with main site navigation on the right. Below the header, the main content areas promote products, blog posts and the social media presence.

Figure 1 A wireframe showing the home page for allthingswesties.com, an isomorphic web app.

This site, allthingswesties.com, is an isomorphic web app. The first time you visit the site, the content is rendered and served to the browser by the server. This is done using server-rendered techniques with NodeJs. As you navigate around the pages, looking for a dog or supplies, each page is rendered by the JavaScript running in the browser using SPA techniques.

In order to make this work, there’s a third piece in the mix. I think of this part as the “isomorphic handoff.” On the server, you save the state of the application and then provide this state to the browser. The browser uses this state to bootstrap the SPA version of the application. Without this isomorphic handoff, the user must wait for the server-rendered page to load, and then wait longer for a complete re-render of the content in the browser.

The All Things Westies app relies on reusing as much code as possible between the server and the browser. It uses JavaScript’s ability to run in multiple environments: JavaScript runs in browsers and also runs on the server via Node.js. JavaScript can run on a lot of other places as well, such as on Internet of Things devices and on mobile devices via React Native, but we’re going to keep the focus on web apps that run in the browser.

An isomorphic app is a web app that blends a server-rendered web app with a single-page application. On the one hand, we want to take advantage of fast perceived performance and SEO-friendly rendering from the server. On the other hand, we want to handle complex user actions in the browser (e.g. opening a modal overlay). We also want to take advantage of the browser push history and XMLHttpRequest (XHR) abilities to hit the server less frequently. Note that XMLHttpRequests (XHR) are also known as Ajax (Aynchronous JavaScript and XML) calls. Ajax calls use the XHR API in browsers.

Many of the concepts in this book could be applied without writing all of the code in JavaScript. Historically, the complexity of running an isomorphic app without being able to reuse code has been prohibitive. It’s possible to server-render your site with Java or Ruby and then transition to a single page app, but it isn’t commonly done because it requires duplicating large portions of code in two languages. This has a high cost in the maintenance of an app.

Figure 2 Isomorphic apps build and deploy the same JavaScript code to both environments.

To see this flow in action, take a look at Figure 2. It shows how the code for All Things Westies gets deployed to the server and the browser. Because we take advantage of JavaScript running in both environments, the same code that runs in the browser, and talks to our API or data source, also runs on the server to talk to our backend. Next we’ll take a look at the specifics of implementing an isomorphic app with JavaScript.

Building our stack

Building an app like All Things Westies requires putting together several well-known technologies. It’s possible to build an isomorphic app using few or no libraries, but it’s highly recommended to take advantage of the JavaScript communities’ efforts in this area.

Make sure any libraries you include in an isomorphic app support running in both the server and browser environments.

The HTML components that display the products and supplies, i.e. the view, will be built with React. We’ll use a Flux-like data architecture via Redux, the current community standard for Flux-like data management in React apps. We’ll explore using webpack to manage what code runs in the browser and to enable running Node module code in the browser.

On the server side, we’ll build a Node.js server using Express to handle routing. We’ll take advantage of React’s ability to render on the server, and use it to build up a complete HTML response that can be served to the browser. Figure 3 shows how all these pieces fit together.

Figure 3 Building the stack for the server and the browser. Entry points, shared code and build types

To make our application work everywhere, we’ll build them in a way that pre-fetches data for our routes using React Router. We’ll also handle differences in environments by building separate code entry points for the server and browser. In cases where code can only be run in the browser, we’ll gate the code or take advantage of the React lifecycle to ensure code won’t run on the server.

Architecture Overview

Earlier in this article, I told you how an isomorphic application is the combination of a server-rendered application and a single-page application, and uses both in the same application architecture. To get a better understanding of how we connect the concepts of a server-rendered application and a single-page application, let’s refer to Figure 4.

Application Flow

Figure 4 Isomorphic application flow

Figure 4 shows all the steps involved in getting an isomorphic app rendered and responding to user input, like a single-page application, starting when the user enters the web address.

Every web app session is initiated when a user navigates to the web app or types the URL into the browser window. For allthingswesties.com, when a user clicks on a link to the app from an email or from searching on Google, the flow on the server goes through the following steps.

The next part of the application cycles the initial load in the browser. We differentiate the first time the user loads the app from subsequent requests because several things will only happen once during this first load.

Initial load is the first time the user interacts with our website. This means the first time the user clicks a link to our site in a Google search, from social media or types it directly into the web address bar.

The first load on the browser begins as soon as the HTML response from the server’s received and the DOM is processed.

At this point, single-page application flow takes over and the app responds to user input, browser events, timers, etc. The user can add products to their cart, navigate around the site, and interact with complex slideshows for pictures of products and dogs.

Handling the server-side request

Now let’s dive in a little deeper and take a closer look at what happens when the server receives the initial request to render the page. First let’s look at what part of the site renders on the server. Figure 5 is like the one at the beginning of the article (Figure 1) but it doesn’t render the twitter widget. The Twitter widget is designed to be loaded in the browser and doesn’t render on the server.

Figure 5 The server-rendered version of the All Things Westies home page.

The server does two important things. First, it fetches the data required for the view. Then it takes that data and uses it to render the DOM. Let’s check out Figure 6, which shows the flow on the server.

Figure 6 Server Render the Page
  1. The server receives a request.
  2. The server fetches the required data for that request. This can be from either a persistent data store like a MySQL or NoSQL database, or from an external API.
  3. Once the data are received, the server can build the HTML. It generates the markup with React’s virtual DOM via the renderToString method.
  4. The server injects the data from step 2 into our HTML, allowing the browser to access it later.
  5. The server responds to the request with our fully built HTML.

Rendering in the Browser

Now let’s look more closely at what happens on the browser. Figure 7 shows the flow in the browser, from the point the browser receives the html to the point it bootstraps the app:

  1. The browser parses the DOM that it has received from the server;
  2. This results in rendering an HTML element; or
  3. Executing JavaScript;
  4. When the browser reaches our entry point for the application, the app bootstraps itself.
Figure 7 Browser Render & Bootstrap

At this point our single-page application flow kicks in again. This is the most straightforward part. It handles user events, makes XHR calls and updates the application as needed.

For more on building exciting isomorphic web apps, download the free first chapter of Isomorphic Development with JavaScript and see this Slideshare presentation for a discount code.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store