Isomorphic web applications, are applications where the web server and the web browser (the client), may share all, or some parts of the web application code. Isomorphic web applications allow us to reap maximum benefits from traditional web application architectures. They provide a better user experience, enhanced discoverability by search engines, and reduced operating costs by sharing parts of the web application code across environments.
Well-established businesses, such as Airbnb, Bloomberg, Capital One, Facebook, Google, Netflix, and Walmart have accepted isomorphic web application development, and with good reason—the financial bottom line.
A study by Walmart found that for every 1 second of improvement, they experienced up to a 2% increase in conversions. In addition to that, they also found that for every 100 milliseconds of improvement, they grew incremental revenue by up to 1%. Source: How Website Speed Affects Conversion Rates (http://www.globaldots.com/how-website-speed-affects-conversion-rates/).
Isomorphic Go is the methodology to create isomorphic web applications using the Go programming language. In this book, we will explore, in-depth, the process of creating an isomorphic web application in Go.
This chapter will cover the following topics:
- Why you should consider isomorphic Go for developing modern web applications
- An overview of the traditional web application architectures
- An introduction to the isomorphic web application architecture
- When to implement isomorphic web applications
- What you should know before learning Isomorphic Go
If this is the case, then why should we focus our attention on Go for creating isomorphic web applications? The answer to this question is manifold. Consider the list of answers provided here, as an initial list, which is the starting point for our discussion:
- Go comes with type checking
- Go comes with a lot of benefits for front-end web development
Go is a language that includes built-in static type checking. The immediate ramification of this fact is that many errors can be caught at compile time itself.
A compiler will turn human readable code, written in a programming language, into machine code. A transpiler is used to transform source code from one programming language into that of another language. The output may or may not be readable by a human, depending on the intent of the transpiler.
Having Go available on the front-end comes with many advantages, including the following:
- A robust standard library
- Code modularization is easy with Go packages
- Go comes with an implicit build system
- Go's concurrency constructs allow us to avoid callback hell
- The concept of concurrency comes built-in with Go
- Go can be utilized for isomorphic web application development
Go comes with a robust standard library that provides a lot of powerful functionality out of the box. For instance, in Go, we can render an inline client-side template without having to include any third-party template library or framework. We will consider an example of how to do this in Chapter 3, Go on the Front-End with GopherJS.
Go has a powerful package implementation that promotes modularization, allowing for far greater code-reuse and maintainability. Also, the Go tool chain includes the
go get command, which allows us to easily fetch official and third-party Go packages.
As long as you follow Go's prescribed conventions, and once you issue the
go build command for your Go application, the implicit build system kicks in. It will build and compile the Go project automatically by examining the dependencies it finds, within the application's Go source code itself. This provides a major productivity boost for the developer.
Soon enough, our list of tasks to delay for execution will grow, and the amount of nested callback functions will grow along with it. This situation is known as callback hell.
We can avoid callback hell in Go, using Go's built-in concurrency constructs.
Go is a modern programming language designed to be relevant in an age of multicore processors and distributed systems. It was designed in a manner where the importance of concurrency was not treated as an afterthought.
In fact, concurrency was so important to the creators of Go, that they built concurrency right into the language itself. In Go, we can avoid callback hell, using Go's built-in concurrency constructs: goroutines and channels. Goroutines are cheap, lightweight threads. Channels, are the conduits that allow for communication between goroutines.
Isomorphic Go is an emerging technology that provides us the essential ingredients needed to create isomorphic web applications, using the powerful and productive features that the Go programming language has to offer. In this book, we will use the functionality from Go's standard library and third-party libraries from the Go community to implement an isomorphic web application.
In order to understand and fully appreciate the architecture of isomorphic web applications, it's insightful to have an understanding of the web application architectures that preceded it. We will cover the major web application architectures that have been prevalent in the industry over the past 25 years.
After all, we can't truly appreciate where we have arrived at, until we've fully acknowledged where we have been. With the monumental changes that have occurred in the web application architecture realm over the years, there is much to acknowledge.
Before we present the isomorphic web application architecture, let's devote some time to review the three traditional web application architectures that came before it:
- The classic web application architecture
- The AJAX web application architecture
- The single-page application (SPA) architecture
We'll identify the advantages and disadvantages for each of the three architectures considered. We will start a wish list of requirements, based on each disadvantage that we identify for a given architecture. After all, a shortcoming is actually an opportunity for improvement.
The classic web application architecture dates back to the early 1990s, when graphical web browsers started to gain traction. When the user interacts with a web server using a web browser, each user interaction, makes a request to a web server using HTTP. Figure 1.1 depicts the classic web application architecture:
Figure 1.1: The Classic Web Application Architecture
The diagram also depicts an HTTP transaction, which consists of a request that's sent from the user's web browser to the web server. Once the web server accepts the request, it will return a corresponding response for that request.
There are two types of resources that the web server can return in the form of a response: a static resource and a dynamic resource.
A static resource is a file. For example, it could be an HTML, JPEG, PDF, or MP4 file that lives on the web server. The server will return the document specified by the request in its response body.
A dynamic resource is a resource that gets built by the server on the fly. An example of a dynamic resource is a search engine's search results page. Usually, the response body of a dynamic request will be formatted in HTML.
When it comes to web applications, we deal with dynamic resources. The web server is serving a web application, and usually the web application contains a controller with the logic that routes the user's request to a specific action to perform on the server. Once the web server is done processing the user's request, the server sends a response back to the client in the form of a web page response.
A server-side programming language (such as Go, Perl, PHP, Python, Ruby, and Java) is used to process the requests sent from the web browser. For example, let's consider we have a server side web application that is used for an e-commerce website.
The web application can route requests by making use of a server-side route handler (as shown in Figure 1.1); the
/product-detail/swiss-army-knife route can be associated to a product detail controller, which will serve an HTML web page response containing the product profile page for the Swiss Army Knife product.
In a classic web application architecture, the code to render the web page lives on the server side, typically consolidated into template files. Rendering the web page response from a set of templates is performed by the template renderer that resides on the server (as shown in Figure 1.1).
The classic web application architecture comes with two major advantages:
- Faster initial page loads
- Greater search engine discoverability
The first primary advantage of the classic web application architecture is that page loads are perceived to be fast by the user since the entire page is rendered at once. This is a result of the web server rendering the web page response, using a template renderer, on the server side itself.
The user does not perceive slowness, since they are delivered the rendered page from the server instantaneously.
Keep in mind that if there is high latency in the server's response time, then the user interaction will come to a grinding halt. In this scenario, the fast initial page load advantage is lost since the user has to stare at a blank screen—waiting for the server to finish processing. This waiting will end with either the web page response being delivered to the user, or the HTTP request timing out—whichever comes first.
The second primary advantage of the classic web application architecture is that this architecture is search engine friendly, since the web application serves up web page responses, in HTML, that can be readily consumed by search engine bots. In addition to this, the server-side route-handler allows for the creation of search engine friendly URLs, that can be associated with a specific server-side controller.
A key factor to making a website friendly to search engines is discoverability. Besides having great content, a search engine friendly website also needs permalinks – web links that are intended to remain in service permanently. Descriptive and well-named URLs can be registered as routes with the server-side's router. These routes end up serving as permalinks, which the search engine bot crawlers can easily index while crawling through the website.
The goal is to have pretty website URLs that can contain meaningful information, which can be easily indexed by a search engine's bot crawler, such as:
The aforementioned permalink is much more easily indexed by a search engine and understood by a human rather than the following one:
We'll be examining the primary disadvantage(s) for each of the traditional web application architectures considered in this chapter. The isomorphic web application architecture section in this chapter, will show us how the isomorphic web application architecture provides a solution for each disadvantage presented and also gather the benefits offered from each of the traditional web application architectures.
The primary disadvantage of the classic web application architecture is that all user interactions, even the most trivial, require a full page reload.
This means that the Document Object Model (DOM), the tree data structure representing the current state of the web page, and the elements that comprise it, are completely wiped out, and recreated again upon each user interaction:
Figure 1.2: A layout diagram of a news website with a comments section and a wireframe depicting the comments section
For example, let's consider that we are reading an article on a news website. Figure 1.2, depicts a layout diagram of the news website (the illustration on the left), with the comments section of the website at the bottom of the web page. Other sections may exist on the news website in the negative (empty) space in the layout.
Figure 1.2 also includes a wireframe design of the news comments section (the illustration on the right), which contains a few sample comments. The ellipses (
...) denotes multiple website comments that are not listed for the sake of brevity.
Let's consider scenario where this particular news article has gone viral and it contains more than 10,000 comments. The comments are paginated, and there are 50 comments displayed per page:
Figure 1.3: The entire web page needs to be refreshed to view the next set of comments
Figure 1.3 depicts the web page for the news website being refreshed (the illustration on the left). Note that the user will perceive the refresh to be quick because the page will instantaneously load (considering that network latency is low). Figure 1.3 also depicts the next batch of 50 articles (the illustration on the right) after the next link has been clicked.
If we were to click on the next link on the paginated navigation control, it would cause a full page reload, which would destroy the DOM and recreate it again. Since the comments are located at the bottom of the screen, upon a full page reload, the scroll position may also change back to the top of the web page, resulting in a poor user experience.
We only wanted to see the next set of comments at the bottom of the page. We didn't intend for the whole web page to reload, but it did, and that's the major limitation of the classic web application architecture.
Figure 1.4: The AJAX web application architecture
The primary advantage of the AJAX web application architecture is that it removes the need to perform a full page reload.
In the scenario that we considered with the news article web page that had 10,000+ comments, we can program the web application to initiate an XHR call when the
Figure 1.5 illustrates this approach. The left-most illustration depicts the comments in the comment section. The middle illustration depicts only the comments section being updated. Finally, the illustration on the right depicts the next batch of comments loaded in the comments section:
Figure 1.5: When the Next link is clicked, only the comments section of the news website is updated, avoiding a full page refresh
As you can see, the primary advantage of this approach is that we avoid the full page reload, which enhances the user experience. Keep in mind that in certain scenarios, such as navigating through different sections of the website, full page reloads may still occur.
The AJAX web application architecture comes with the following disadvantages:
- Handling the mental context switch between two programming languages
- The complexity introduced by performing piecemeal client-side rendering
- The duplication of efforts
This causes a mental context shift for the full stack developer that is tasked with maintaining the client side and the server side of the codebase. One way for organizations to immediately address the issue of mental context shifts is to reach into the pocketbooks. If the organization can afford to do this, it could take the hit in increased operating costs and dedicate at least one developer to the front-end and one developer to the back-end.
In addition to introducing the mental context shift of handling two different programming languages, we have now increased the level of rendering complexity. In the classic web application architecture, the rendered web page that was received from the server response was never mutated. In fact, it was wiped out once a new page request was initiated.
Now, we are re-rendering portions of the web page in a piecemeal fashion from the client side, which requires us to implement more logic to make (and keep track of) subsequent updates to the web page.
The AJAX web application architecture introduces a duplication of efforts between the server side and the client side. Let's say that we wanted to add a new comment to the news article. Once we fill out the form, to add the new comment, we can initiate an XHR call, which will send the new comment, that is to be added, to the server. The server-side web application can then persist the new comment to the database, where all comments are stored. Instead of refreshing the entire web page, we can immediately update the comment section to include the new comment that was just added.
At this point, we have introduced a duplication of efforts in two programming languages spread across two different operating environments. Besides the example we just considered, there may be other scenarios that require duplication of efforts when going down this architectural path. This happens to be a major disadvantage of the AJAX web application architecture.
In 2004, the World Wide Web Consortium (W3C) started working on the new HTML standard, which was to be the precursor to HTML5. In 2010, HTML5 started to pick up speed, and features from the specification started to make their way into the major web browsers and the HTML5 functionality became very popular.
Figure 1.6: The Single Page Application (SPA) Architecture
The SPA architecture removes the duplication of efforts for user interface responsibilities. It does so by consolidating all UI code to the client. Doing so eliminates the duplication of efforts on the server side in terms of the user interface. As depicted in Figure 1.6, the responsibility for the user interface rests solely with the client.
Minification is the process of removing any unnecessary characters from the source code, which may include renaming identifiers in the source code without changing the functionality of the source code, in order to reduce its storage footprint.
The primary advantage of the SPA architecture is that it provides client-side routing, preventing full page reloads. Client-side routing involves intercepting the click event of hyperlinks on a given web page, so that they don't initiate a new HTTP request to the web server. The client-side router associates a given route with a client-side route handler that is responsible for servicing the route.
The experience is seamless from the user's perspective, since the user will not experience the sudden white flash that is encountered on a full page reload.
The SPA architecture comes with the following disadvantages:
- The initial page loads are perceived to be slower
- Reduced search engine discoverability
Figure 1.7: The initial page load is perceived to be slow since the user is greeted with a loading indicator instead of the rendered web page
Figure 1.7 includes an illustration (on the left) that depicts the loading indicator, and an illustration (on the right) that depicts the layout of the loaded web page. It is important to note that, depending on the SPA implementation, there may be more than one loading indicator spread across the individual sections that make up the web page.
I'm sure that, in your own web browsing travels, you have probably used web applications that have contained these loading spinners. We can agree, from the user's perspective, that ideally we would rather want to see the rendered output instead of the spinning wheel.
The use of the SPA architecture may reduce search engine discoverability. Because of the dynamic nature of rendering content on the client side, some SPA implementations may not produce well-formed HTML content that can be easily consumed by search engine bot crawlers that are used to consuming an initial web page response only.
In addition to this, SPA implementations handle routes using fragment identifiers, strings that refer to a resource, after the hash mark (#) of a URL. This approach is not search engine friendly.
Let's return to our e-commerce web application example. In the classic and AJAX web application architectures, our web application could have the following URL:
In the case of the SPA implementation, the URL, with a fragment identifer, could look like this:
This URL would be difficult for a search engine bot crawler to index because the fragment identifier (the characters after the hash symbol) is meant to specify a location within a given web page.
Fragment identifiers were designed to provide links within sections of an individual web page. The fragment identifier influences the web browser's history since we can tack on unique identifiers to the URL. This effectively, prevents the user from encountering a full page reload.
The shortcoming of this approach is that fragment identifiers are not included in the HTTP request, so from a web server's perspective, the URL,
http://igweb.kamesh.com/webapp#orange, and the URL,
http://igweb.kamesh.com/webapp#apple, are pointing to the same resource:
The search engine bot crawler would have to be implemented in a more sophisticated manner to handle the complexity of indexing websites containing fragment identifiers. Although Google has made considerable progress on this problem, implementing URLs, without the fragment identifiers, is still the recommended best practice to ensure websites are easily indexed by search engines.
It is important to note is that in some cases, the SPA architecture may overcome this disadvantage, using more modern practices. For example, more recent SPA implementations avoid fragment identifiers altogether, using the web browser's History API to have more search engine friendly URLs.
The isomorphic web application architecture consists of implementing two web applications, one on the server side and one on the client side, using the same programming language and reusing code across the two environments:
Figure 1.8: The Isomorphic Web Application Architecture
As depicted in Figure 1.8, business logic can be shared across environments. For example, if we had defined a
Product struct to model a product for our e-commerce website, both the server-side and client-side applications can be made aware of it.
In addition to this, a template renderer exists on both the server side and the client side, so that templates can also be rendered across environments, making templates isomorphic.
The term isomorphic can be used to describe anything (business logic, templates, template functions, and validation logic) that can be shared across environments.
The server-side route handler is responsible for servicing routes on the server side and the client-side route handler is responsible for servicing routes on the client side. When a user initially accesses a website implemented using isomorphic web application architecture, the server-side route handler kicks in and generates a web page response using the server-side template renderer.
Subsequent user interactions with the website are performed in the SPA mode using client-side routing. The client-side route handler is responsible for servicing a given client-side route and rendering content to the web page (the user interface) using the client-side template renderer.
The client-side application can initiate a XHR request to a Rest API endpoint on the web server, retrieve data from the server's response, and render content on the web page using the client-side template renderer.
An Isomorphic Go web application may optionally utilize a WebSocket connection, as shown in Figure 1.8, for persistent, bidirectional communication between the web server and the web browser. Isomorphic Go web applications have the added benefit of sending and receiving data in the
gob format—Go's format for binary encoded data. Encoding and decoding data to the
gob format can be done using the
encoding/gob package from the standard library.
The primary advantage of the
gob format is its lower storage footprint. JSON data is in text format, and it's understood that data formatted as text requires a heavier storage footprint when compared with a binary encoded format. With smaller data payloads exchanged between the client and server, the web application can benefit with faster response times when transferring data.
The Isomorphic Web Application Architecture offers a solution for all of the disadvantages found in the three traditional web application architectures. Let's take stock of the items that we've placed on our wish list:
- To enhance the user experience, clicking a link on the website should not cause a full page reload.
- To increase maintainability, there should be a single, unified, project codebase that is implemented in a single programming language.
- To increase efficiency, there should be a mechanism to perform distributed template rendering.
- To increase productivity, there should be a means to share and reuse code across environments, to avoid the duplication of efforts.
- To make the best first impression, the website should readily display content to the user.
- To promote discoverability, the website should provide well-formed HTML content that is easily consumed by search engine bots. The website should also contain links that are easily indexed by search engine bots.
Now, it's time to examine how the isomorphic web application architecture fulfills each item that has been placed on our wish list.
After the initial server-side rendered web page response, the isomorphic web application architecture enhances the user experience by running in the SPA mode. Client-side routing is used for subsequent user interactions with the website, preventing full page reloads and enhancing the user experience of the website.
Maintainability of the project codebase is strengthened by the isomorphic web application architecture due to the fact that a single programming language is used to implement both the client-side and server-side web applications. This prevents the mental context shifts that occur when dealing with two different programming languages across environments.
The isomorphic web application architecture increases the efficiency of rendering content by providing a mechanism for distributed template rendering—the isomorphic template renderer. With a template renderer present on both the server side and the client side, as depicted in Figure 1.8, templates can easily be reused across environments.
The single unified codebase that is the hallmark of the isomorphic web application architecture provides many opportunities to share code across environments. For example, form validation logic can be shared across environments, allowing a web form to be validated, both on the client side and the server side using the same validation logic. It is also possible to share models and templates across the client and the server.
The isomorphic web application architecture's usage of server-side rendering for the initial web page response, guarantees that the user will see content immediately upon accessing the website. For the first encounter with the user, the isomorphic web application architecture takes a page out of the classic web application architecture's playbook for providing the initial web page response.
This is a welcome benefit to the user, since content is displayed to them instantly and the user will perceive a fast page load as a result of this. This is a sharp contrast to the SPA architecture, where the user would have to wait for the client-side application to bootstrap before seeing the web page's content appear on the screen.
The isomorphic web application architecture promotes discoverability, since it can easily provide well-formed HTML content. Keep in mind that the rendered output of Go templates is HTML.
Another means by which the isomorphic web application architecture promotes discoverability is that well-formed URLs can be defined by the application's route handlers (both on the server side and client side) and these URLs can easily be indexed by search engine bot crawlers.
This is possible because the route handler implemented on the client side makes use of the web browser's History API to match the same routes that are defined on the server side. For example, the
/product-detail/swiss-army-knife route for the Swiss Army Knife product detail page can be registered by both the server-side and the client-side routers.
Now it's time to see the isomorphic web application architecture in action. A live demo of IGWEB, the website that we will be implementing over the course of this book, is available at http://igweb.kamesh.com. Figure 1.9 is a screenshot of the website's home page:
Figure 1.9: IGWEB: A website implemented with Isomorphic Go
Notice that the content in the above the fold area (the area that is visible in the browser window) is displayed instantly. Also, take note of the responsiveness of the website when navigating to different sections of the website by clicking on the links in the navigation menu. We'll provide you with a detailed introduction to the IGWEB project in the next chapter.
At the time of writing, IGWEB has been verified to function in the following web browsers: Google Chrome version 62.0, Apple Safari version 9.1.1, Mozilla Firefox 57.0, and Microsoft Edge 15.0. It is recommended that you use a web browser that has the same version, or above the version, provided in this list.
The methodology to develop an isomorphic web application using Go, that is presented in this book, has proven, measurable benefits with regard to providing an enhanced user experience.
We can use the Google PageSpeed Insights tool (https://developers.google.com/speed/pagespeed/insights/) to evaluate the performance of IGWEB's home page. The tool measures how well a web page delivers a good user experience, on a scale of 0 to 100, based on various criteria, namely the organization of web page content, size of static assets, and time taken to render the web page:
Figure 1.10: The result of running the IGWEB home page through the Google PageSpeed Insights tool
Figure 1.10 is a screenshot that shows the result of evaluating the desktop edition of IGWEB. At the time of writing, IGWEB scores 97/100 for the desktop browsing experience, and 91/100 for the mobile browsing experience. According to the tool, the 90+ score attained for both the desktop and mobile editions indicates that the IGWEB home page applies most performance best practices and should deliver a good user experience.
The word isomorphism comes from mathematics. In Greek, iso means equal and morphosis means to form or to shape.
In my opinion, the term isomorphic is more appropriate, while the term universal introduces ambiguity. The ambiguity stems from the fact that the term universal carries some baggage along with it.
Therefore, using the term universal is ambiguous, and requires extra detail, to determine the context in which it is used. For this reason, the preferred term that will be used in this book is isomorphic.
We assume that the reader has some level of prior programming experience in Go, or some other server-side programming language.
If you have never programmed in Go, I would recommend that you refer to A Tour of Go available at: https://tour.golang.org.
For a more in-depth study of fundamental Go concepts, I would recommend that you take my video course, Go Essentials For Full Stack Web Development,Packt Publishing, available at https://www.packtpub.com/web-development/go-essentials-full-stack-web-development-video.
In this chapter, we provided an introduction to Isomorphic Go. We covered the many advantages that the Go programming language provides, and why it makes a compelling choice for the creation of isomorphic web applications.
We reviewed the traditional web application architectures, which included the classic web application architecture, the AJAX application architecture, and the SPA architecture. We identified the advantages and disadvantages of each traditional architecture. We introduced the isomorphic web application architecture and presented how it solved all the shortcomings of the traditional architectures.
We presented a live demo of IGWEB, an Isomorphic Go website, and introduced you to the Google PageSpeed Insight tool to measure web page performance. Finally, we provided you with some background on the term isomorphic and the items that you need to know to make the most out of understanding the material covered in this book.
In Chapter 2, The Isomorphic Go Toolchain, we will introduce you to the key technologies used to develop Isomorphic Go web applications. We will also introduce you to IGWEB, the Isomorphic Go website, that we will be building over the course of this book.