A collection of notes & ideas from the Quartz Engineering team
The good folks at WordPress.com invited me to give a brief talk during the VIP Developer Workshop earlier this month in Napa. It went a little something like this…
Hi, I’m Josh Kadis. I work for Quartz. We are part of Atlantic Media.
Perhaps you have seen our website. It’s a responsive single-page web app that was designed tablet-first. We use WordPress.com VIP for the publishing interface and API, and Backbone.js for the web app. We also have a self-hosted user accounts system to manage newsletter subscriptions, starred posts, and some forthcoming features.
We ran into a few unexpected issues during development and there weren’t a lot of examples of WordPress-powered web apps to guide us.
WordPress.com offers a JSON API feature within the excellent Jetpack plugin. But we opted to build our own custom API for a couple of reasons. First, a lot the WP_Queries that are really essential to Quartz depend on post meta fields, custom post types, and other stuff that differs from the usual arguments that get passed to WP_Query. So we used custom rewrite rules to namespace the API endpoints inside qz.com/api and avoid using URL parameters that would prevent the requests from being cached.
The other reason for the custom API was that we wanted to manipulate post metadata before returning it in the API - for instance, we return URLs for multiple sizes of featured images. And we can improve performance by returning HTML fragments that are built on the server from the post metadata. If we used Jetpack’s JSON API, for example, that work would fall on the client-side web app.
If you know what Backbone models you’ll need to initialize your collection, you can speed things up by bootstrapping them as JSON data inside a <script> tag in your markup instead of making an AJAX call. But unless your markup also includes those posts as HTML, they won’t be indexed by search engines. Unfortunately, printing a bunch of news stories twice (once as JSON, once as HTML) frequently caused us to exceed Google’s 256kb page size limit. So now we’re printing the HTML, making an AJAX call for the JSON data, then overwriting the original HTML when the app initializes.
For a WordPress web app, post previews get complicated in a hurry. There are different cases for unsaved posts, unpublished drafts, and changes to published posts. You need to parse the URL parameters, confirm that the user has permission to view the preview and, in some cases, verify a nonce. Plus, in our case, we have to make a cross-domain AJAX call. Doing all that in the web app is unnecessarily complex, and WordPress already gives you the is_preview() conditional. So this is case where we do bootstrap JSON in the markup, the previous slide notwithstanding.
Since we host some of our files on a subdomain separate from WordPress.com, after launch we found that a lot of the commits to our VIP repository were just changing the version number of the files we wanted to load. But someone on the VIP team still needs to review and approve that commit. There’s no reason to go through all that, so we wrote a plugin using the excellent Settings API that allows us to specify a version number from within WP Admin.
So we end up with one version number hard-coded in the theme and another one set in wp_options via the plugin. By default, we load the higher of the two version numbers. If there’s a deploy that only affects our self-hosted files, we increment the wp_options version number using the plugin. If there’s a deploy that also includes changes in the WP theme, we increment the hard-coded version number. If we need to roll back, we can override the default and force the theme to load either the plugin version number or the hard-coded version number.
There you have it.
- Josh