Web Application Types (Part 2): The Modern Single-Page App
Phil Gambling

In this post, I'll pick up where Brandon left off in Web Application Types (Part 1) and take a deeper dive into the client-side single-page application, commonly abbreviated as “SPA”. What is considered an SPA? What are important choices to be made when building one? How do you deploy it? When is an SPA a good choice or a bad choice? Let’s dive in and find out.

What exactly is a Single-Page Application?

An SPA can be thought of as a thick client that uses the browser as an application platform. The responsibility for displaying views to the user is handled in the browser and the back end’s sole purpose is to respond to requests for data. The SPA uses the responses from the server and updates the view by modifying the live HTML client-side to reflect those responses.

It’s a “single” page in that only one static HTML file needs to be sent by the server, unlike traditional web pages that serve separate HTML files for each URL. Other static files, notably JavaScript, stylesheets, and images, linked to by the original HTML file are also loaded. After the initial page load, navigation within the app no longer triggers a full page refresh, and routing is handled entirely client-side. This enables a very flexible deployment model because all of an SPA’s files can be served from the same server providing other data and services, a completely different server, or several in the case of content delivery networks. The ability to simply serve files to a web browser is all that is required to host an SPA.

Considerations When Building an SPA

At its most basic, a single-page app consists of three files: an index.html file, a JavaScript file, and a stylesheet. However, in reality (unfortunately) there is more to it than that. Three major choices need to be made up front: the programming language, an application framework, and a build process.

Programming Language

The choice of programming language may seem obvious when writing a client-side web application: It’s JavaScript, right? Well yes and no. JavaScript is what will ultimately run in the browser, but many languages can be compiled to JavaScript. Today, even JavaScript is compiled to JavaScript, if you want to use the latest and greatest features of the language before they are supported in all browsers. Other popular choices are TypeScript, ClojureScript, CoffeeScript, and Elm. However, chances are that if you have a preferred programming language, then there will be a compiler for it that targets JavaScript.

At Expero, our current preference is ES6 (the latest published JavaScript standard) with heavy use of upcoming features from ES7 and beyond. This is made possible through the Babel transpiler (JS to JS compilation), which outputs JavaScript using a widely supported subset. We also have experience with and enjoy TypeScript and ClojureScript too!

Application Framework

Framework is an overused term that can have many meanings today. It could refer to a JavaScript framework, a style framework, server framework, a testing framework, etc. For the SPA, you almost always need a client-side JavaScript framework like React, Angular, or Ember unless you are building a very trivial application. The purpose of a JavaScript framework is to manage application state, events, and updates to the view. Remember that the single-page app is really a thick client, and while you could build your own custom framework to handle these concerns, most will benefit from using a proven and mature framework to handle these common concerns in an efficient manner.

I won’t attempt to suggest the “right” framework choice here as the Internet is full of debates on the subject. The right choice depends on a lot of factors, including personal preferences, team skill sets, and project requirements. There is no one-size-fits-all. That being said, for the kind of complex interfaces Expero typically tackles, we prefer to use React paired with Redux for state management, but we routinely tackle projects using other frameworks as well, notably Angular. React is backed by Facebook and brings with it a thriving ecosystem of libraries and tools to add on functionality as you go.

Build Process

The primary tasks of the build process are code compilation, file bundling, and minification. The end result will be an index.html and the required JavaScript, CSS, and other static assets, like images and fonts, that are needed to deploy the web application.

One could build an SPA without any build process, using only the most commonly supported features of JavaScript (ES5) and a mess of JS and CSS files. However as mentioned earlier, there are features of JavaScript that we'd like to take advantage of that aren't widely supported yet, so we have to use a compiler, such as Babel, that takes the language we'd like to program against and compile it down to a lowest common denominator. Similarly, CSS preprocessors, like LESS or SASS, are commonly used to provide higher-level features for stylesheet development. The use of these, as with a JS compiler, necessitates some build process.

Furthermore, the naive construction of a web application results in thousands of files that would result in a very inefficient delivery to the browser, so we want to compress them both in number and size.

Another common task of the build process is to include some unique identifier in the deployment file names, commonly a hash of the contents or a timestamp. This enables static files to be cached for a long time, while still providing a way to break the cache when changes are made (more on this later). This has two major benefits: 1. Server load is reduced thanks to caches short-circuiting frequent file requests, and 2. Your support team will never have to tell your customers to “clear their browser cache” in order to see updates.

Common build tools in use today include Webpack, Grunt, and Gulp. The choice of framework or programming language may already dictate your chosen build tool. Angular, for example, provides its own build system. Webpack is often used for React-based applications.

Flexible Deployment

SPAs are very flexible when it comes to deploying to a server. As long as the server can deliver a collection of static files, then it can host a single-page application. This is important because it means you can cleanly decouple your back end from the client’s view and architect the back end any way you choose. You may want to keep things simple and host the front end on the same server that handles requests for data or you might have a large ecosystem of load-balanced microservices and choose to serve the front end from a highly available CDN. It makes little difference to the overall architecture and design of the client-side application.

We’re fans of Amazon Web Services. Here’s an example deployment using Amazon’s S3 service.

In this model, the concerns of serving www.foo.com are completely separate from any requests for data through an API server. Both can be managed and optimized for their intended purpose. In the case of the file server, S3 buckets can be configured for website hosting and serve all the static files that make up the SPA. As mentioned earlier, aggressive caching headers can be set to further speed up client load times and reduce server load if unique hashes (or some other cache break technique) were implemented as part of the build process.

This separation completely eliminates a chunk of work for the back end. Pure data services typically should not allow the client to perform any caching in order for client applications to always receive fresh data. Furthermore, the scaling and division of responsibilities among services is not mixed up with the concerns of serving the client-facing URL.

This is all made possible thanks to Cross-Origin Resource Sharing (CORS) mechanisms, now commonly available in all browsers, which enable the browser to make requests to different domains from the one that originally served the web application files. Before CORS, the browser only allowed non-GET requests to the original domain that served the JavaScript file making the request. CORS is a mechanism to safely permit requests to trusted domains while still maintaining the same origin policy for all other domains.

Do you need a single-page app?

Single-page apps are a must when a responsive and interactive in-browser user experience is required. The classic server-based approach to updating a page in response to a user interaction is painfully slow by today’s standards. Each change to the view requires a complete page reload, which incurs a lot of overhead. The single-page app only pays the page load penalty one time, and further changes are incremental modifications to the live document.

The separation between front end and back end also tends to lead to a clean API that can be reused for native mobile applications. In many cases, the single-page app itself can be repackaged as the mobile application.

However, It’s not always the right choice, and the choice depends on the project itself. Remember that a single-page app really is a full-blown application and not just a website. In the case of mostly static content, like a marketing site, an SPA is far too complex and a simple static website is enough. Blogs and other sites where many users need to all collaborate and publish content are better served by the traditional server-side rendered approach. SPAs are especially unfriendly towards search engine results due to the nature of rendering content after loading the initial HTML file. Some search engine web crawlers, like those at Google, will actually execute JavaScript content, but the results are inconsistent. There are ways around this, such as an isomorphic single-page app, but the increased complexity probably isn’t worth the cost if search engine optimization is a priority.

Security is also a concern with an SPA. All the code running on the browser can be easily inspected by a tech-savvy user. A build process that minifies the code may provide some level of obfuscation, but that’s not true security. Any sensitive information, calculations, etc. are best kept exclusively on the server. User input validation is a common example of something that should ALWAYS run on a server and not depend on client code that could be bypassed in order to prevent bad things from happening. This doesn’t necessarily rule out an SPA, but it does mean care must be taken when designing the system in order to keep sensitive code away from the client.

Conclusion

The modern single-page application has become a first-class citizen next to native desktop and mobile applications in what is now possible with a web browser. This is a constantly evolving field, and what is trendy and in fashion today might be old news six months to a year later. At Expero, we love to keep up with this ever-changing field and bring that experience to our clients.

This post has only scratched the surface on today’s single-page applications. Since SPAs are still relatively new, many existing applications are still using older approaches and struggling to deliver performant user experiences. If you're sold on building an SPA, then keep a lookout for an upcoming post from us that discusses what you should consider when modernizing your existing application.

RECENT POSTS FROM
THIS AUTHOR