JavaScript Configuration Object Pattern: JSON Saves the Day

So you’re building a modern web app? That means you’re likely running a variety of client-side libraries and custom business logic in JavaScript. And one of the first hurdles you run into is “Hey, I need some data from the server injected in my JavaScript.” Some developers, when posed this problem, have an unproductive response: They start dynamically generating strings of JavaScript on the server so they can inject custom logic onto the page.

No surprise. This is arguably the path of least resistance. But as we’ll see below, it’s toxic because it’s hard to maintain. And it’s not performant. And…okay, let’s hold off on the justification for a second and review a concrete example.

Injecting Server-side Data Into JavaScript

Imagine you’ve built a content management system that is run by multiple clients. Each client configures their Google Analytics Key within an admin console, which you then save to the database. Ultimately, this data needs to be injected into JavaScript. One approach is to build the entire Google Analytics script in a string on the server and inject it onto the page. It looks like this in C#:

			string googleAnalyticsScript = @"var _gaq = _gaq || [];
				_gaq.push(['_setAccount', '" + googleAnalyticsKey + @"']);
				_gaq.push(['_trackPageview']);

			  (function() {
				var ga = document.createElement('script');
				ga.type = 'text/javascript'; ga.async = true;
				ga.src = ('https:' == document.location.protocol ?
				'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
				var s = document.getElementsByTagName('script')[0];
				s.parentNode.insertBefore(ga, s);
				})();";

Note how hard this is to read. Since the entire script is now stored in a string on the server, it’s a wall of text with no code coloring except for the googleAnalyticsKey variable that’s being injected into the script. There’s a variety of other issues with this approach that we’ll get to shortly.

Configuration Object Pattern

Thankfully, a much cleaner option exists using what I call the JavaScript configuration object pattern. Begin by placing the static JavaScript in a .js file. Use variables as placeholders for any dynamic data:

[pageview url=”http://jsfiddle.net/housecor/KfZL3/embedded/js” height=”245″]

Note the reference to websiteSetup.GoogleAnalyticsKey above on line two. Where’s that data defined? We define it separately by creating a data structure on the server and serializing it to JSON using a library. As we’re about to see, this is trivial since virtually all popular programming languages have libraries for serializing objects to JSON. Let’s take a look at how we pull this off in C#.

First, define a class called WebSiteSetup with the necessary properties. In this example we only need a single property to store the Google Analytics Key, but the idea scales well if you have a complex dataset to inject.

	public class WebsiteSetup
	{
		public string GoogleAnalyticsKey;
	}

Alternatively, in C# you could simply serialize an anonymous type. And with a single value like this that arguably makes the most sense.

Once you’ve defined your type, use a JSON serialization library on the server to convert an instance of this type into JSON. In C#, there are many JSON serialization libraries but in this example I’ll use JavaScriptSerializer since it’s shipped with the .NET framework.

			var websiteSetup = new WebsiteSetup()
			{
				GoogleAnalyticsKey = "GoogleAnalyticsKeyFromDatabaseSetHere"
			};

			var json = new JavaScriptSerializer().Serialize(websiteSetup);

The snippet above will generate the following JSON string:

"{\"GoogleAnalyticsKey\":\"GoogleAnalyticsIdFromDatabaseSetHere\"}"

Now it’s just a matter of injecting this string into the head of your page. This piece will vary depending on your server-side technology, but in ASP.NET WebForms you’d do something like this:

			var websiteSetup = "var websiteSetup = " + json + ";";
			Page.ClientScript.RegisterClientScriptBlock(this.GetType(), "WebSiteSetupScript", websiteSetup, true);

The idea here is simple: inject the data that your static .js script will need into the head of your page. Just make sure this JSON is injected before your static .js script. The beauty of this approach is we’ve separated our concerns. Configuration data is separate from the definition of the static third party script. And we’ve gained many other benefits as we’ll see below.

JavaScript belongs in .JS Files

So yes, there are a few more moving parts here. And going through these extra steps to inject a single piece of data into a small script that Google provides may seem like overkill to you. But imagine you’re building client-side charts that require dozens of datapoints from the server for configuration and rendering. Then this approach really shines.

Regardless, chances are you’ll need a variety of data from the server on page load for various JavaScript files. Defining a single setup object that contains all this data allows you to keep all static JavaScript within .js files. The big rule here is to stay native. By native, I mean JavaScript belongs in .js files, not some string written in a server-side language. There’s a variety of benefits to staying native by using the configuration object pattern above.

  1. Separation of concerns – Client-side code should be written *directly* in a client-side language: JS. Avoid using a server-side language to generate JS in a string. Instead, inject dynamic JSON which should be created by serializing a server-side type with a library. This is the approach outlined above.
  2. Caching – JavaScript files are cached by browsers, saving bandwidth and reducing subsequent page load times. In contrast, dynamic JS is downloaded on every server request. So strive to minimize the amount of dynamic JavaScript you send to the client by keeping static and dynamic JavaScript separate.
  3. Minimizes string parsing overhead – Dynamic JavaScript requires the server to render strings of JavaScript on every page request. Rendering a small snippet of JSON that only contains the dynamic data minimizes string parsing overhead.
  4. Code coloring – Note how much easier it is to read the clean native .js file than the string example we reviewed at the beginning of the post. The IDE colors variables, operators, strings, and function declarations separately which aids comprehension.
  5. Syntax checking – If you make a typo writing native JavaScript, your IDE will help you out by underlining the mistake. And you can only enjoy intellisense support when writing native JavaScript in a .js file.
  6. Reusable – The separate .js file makes it trivial to utilize elsewhere. JavaScript written in a string on the server-side is less likely to be reused.
  7. Reduced payload – JS can be minified to save bandwidth, unified to reduce HTTP requests, and obfuscated as desired.
  8. Less abstraction – Building dynamic JS via strings adds a confusing abstraction layer. The reader has to understand both the string parsing rules of the server-side language and JavaScript. This extra layer of abstraction hinders comprehension and maintenance. While the computer doesn’t mind, the maintenance programmer will certainly struggle. Remember:

    Programming is the art of telling another human what one wants the computer to do.
    Donald Knuth

StackOverflow Example

StackOverflow uses the configuration object pattern as well. Visit StackOverflow.com and view source. Search for StackExchange.init(. They simply serialize an object to JSON so that their .JS files have the dynamic data necessary to perform the desired client-side behavior. So hey, we’re in good company here.

Clean Code is for Humans

This conversation is a sampling of the topics I cover in my new Pluralsight course “Clean Code: Writing Code for Humans”. If you’re interested in becoming a better developer and building a vocabulary for evaluating code quality, give it a look!

Clean Code on Pluralsight

Do you find this approach useful? Have another way you solve this? Chime in via the comments below.

13 replies on “JavaScript Configuration Object Pattern: JSON Saves the Day”

  1. I think I agree with you on the general principle, but I think this isn’t a good example of this practice. Sometimes things are so simple, it’s best to keep them simple for the sake of yourself and others.

    The simplest way to handle a Google Analytics code is to copy-paste the code from Google to your website. It’s painless, it’s easy, and everybody knows what to expect. Even in the case of when the GA code is dynamic, we’re talking about a single string in an understandable place. I’ve had to handle a dynamic GA code, and for fairness’ sake I’ve put up a gist:

    https://gist.github.com/darrencauthon/6939828

    The thing about passing a variable through client is… I think it’s generally a pain to have to maintain things where something way over here (left hand waggling) is tied to something waaaay over there (right handle wiggling), but the only tie is in the programmer’s brain. That’s the stuff that’s prone to break as soon as time passes and someone else jumps into the project.

    But beyond that… aren’t you still doing the same thing? You don’t want to avoid string parsing overhead, but your solution still does that (var websiteSetup = “var websiteSetup = ” + json + “;”;). Plus, your solution now includes JSON parsing, which probably makes your solution slower than the original sample code. You want less abstraction, but you created a C# class just for the passing of the string (which you serialize and pass as a string). You want separation of concerns, but now your JS is bound to the C# class definition and you’re passing data through global JS variables. You want JS to be in .js files, but you’re still embedding a line of JS in the header of every page.

    Again, I think there’s something to be said for this pattern, and I’ve done similar sorts of things in more complex situations than this. But for Google Analytics, I think nothing’s more reusable than the copy button they put next to every GA code that’s released.

  2. Thanks for the reply Darren. I totally agree that this pattern is overkill if you only have a single value from the server to inject. But in rich client-side applications, that’s the exception, not the rule. Most apps I write require a variety of parameters from the server injected into JavaScript. I used a simple example here to keep the code snippets approachable.

    Regarding your point about copy and paste from Google being easiest, I agree. But that doesn’t support the use case I’m discussing: Dynamic settings from the server injected into static JS.

    If you don’t want to create a class on the server to represent the configuration object, anonymous types are an alternative in C#.

    And yes, anytime there’s dynamic JavaScript behavior involved, string parsing is required. The point is, string parsing is minimized by keeping as much JavaScript as possible in static .js files. Rendering a small snippet of JSON that only contains the dynamic data minimizes string parsing overhead and maximizes the amount of JavaScript that is cached by the client.

  3. I’ve gone through a few more complex examples, and it’s been my experience that once things go farther than this simple example, the entire process can or should be entirely in JS.

    For example, let’s say that we need to load four complex objects as some sort of “setting” or data. In those cases, I’ve had better success having the browser make an AJAX call to retrieve those settings on page load. There’s one path in for the data(*), you can use common caching schemes to prevent the call on every page hit (though I’ve only really needed this on SPAs), there’s better separation of concerns (no use of the global namespace), you don’t have to do any string parsing, and you don’t have a mini race condition (better get that global set before the JS is run!). Frameworks like MVC or Rails are great if you want to say, “Just serve this object as data on this route” as opposed to using HTML as a data transport mechanism.

    I guess the general rule of thumb I have is: Don’t dump javascript data objects in a script tag as global objects for consumption later.

    (*) I’ve seen the pattern of rendering JSON blocks on a web page a few times, and I’ve seen them blow up as soon as we get to the first bit of “dynamic” loading or refreshing. The global object can cut off some perceived page-loading time. We’ve always dropped the global object for an AJAX call. But again, this is for more complex examples – not a GA code.

    1. As I’m sure you know, there’s no namespacing in JS. The example above uses poor man’s namespacing by wrapping the configuration object in an object called “webSiteSetup”. This is no more “global” than any other JS you would write in your scenario since JS provides no namespacing. Google mimics the approach I outline above, by placing their config object under window.google.

      The chance of a “mini race condition” is actually higher in your proposed scenario. Your AJAX call has to complete before depending code can run, so you need to use promises or callbacks to avoid a race condition in your scenario. With the configuration object pattern above, all other JS calls are simply defined below the configuration object initialization. No extra plumbing on the client is required.

      Finally, there’s a simple maxim at play here: The server should send required known data on page load if possible. Asking the server for data via AJAX that could have been received on page load is unnecessarily complicated and inefficient: It requires an extra unnecessary HTTP round trip and additional client-side complexity to make the call and marshal the data.

      So while the alternative you proposed certainly works, StackOverflow and Google use the configuration object pattern I’m discussing for good reason.

    1. Ignore Google Analytics as an example. It’s not the intended focus of this post. The point of this post is to outline a design pattern for using server-side config data to provide dynamic JavaScript behavior/configuration. Consider alternative examples. Imagine you’re building JavaScript charts or varying application behavior based on user roles. You need dynamic JavaScript data to pull this off. The question is how.

      The valid alternatives are:

      1. Dynamic JavaScript built as a string on the server
      2. Making an Ajax call upon load to get config data (which Darren already mentioned above).

      I’ve argued above that these solutions aren’t ideal, but if you have another alternative to the configuration object pattern to consider I’d love to hear it.

        1. Interesting. Thanks for sharing. I follow what Google is doing in the link you provided – they’re specifying a standard way for interacting with their proprietary code via JSON. But are you clear what the intent is of the w3 document you linked to?

  4. Well Cory I won’t belt you over the head for using a convoluted example (glasshouse dweller).
    But I have a similar issue, to wit:
    We are using AJAX to call WCF services (they could be WEB API as well) from various sub folders.
    So the names or the DOM OBJECT window.location.HREF
    would be :
    http://localhost:80/myVirtualDirectory/Reporting/reporting.aspx
    OR
    http://localhost:80/myVirtualDirectory/Sales/sales.aspx
    My service actually resides at:
    http://localhost:80/myVirtualDirectory/
    When I am calling from a “sub folder” the service cannot be found or I have to put *.svc files in parent and all sub folders.
    So need all my AJAX “url” to start with
    http://localhost:80/myVirtualDirectory/
    Only I don’t know what myVirtualDirectory is going to be as we deploy this application to our customers.
    I wrote a scriptblock for to push this out as:
    var urlBase = “http://localhost:80/myVirtualDirectory/”
    The code in C# was like this (abridged)
    “var urlBase = ‘//’+window.location.host+” + Request.ApplicationName + ” ‘/’ “;
    Is there a better way?

    Regards
    GregJF

    1. Hi Greg,

      Great question. Yep, that’s really the same idea as the configuration object, except your data isn’t wrapped in an object literal so you accept a higher risk of naming collisions.

      Regarding the root reason you need the path at all, I prefer to consistently deploy to the web root so that I can consistently use absolute rather than relative paths. Thus I can start all paths with /my/path and avoid the complexity of injecting a path from the server.

      Cory

Comments are closed.