I just wanted to take a moment to talk about how I approached the hot word "headless Drupal" on my blog. It uses some sort of "headless" communication with the Drupal site, but it also leverages Drupal in a standard way. For different reasons. (by the way, if you are interested in "headless Drupal", there is a groups.drupal.org page about the subject.)
First of all, let's examine in what way this simple blog is headless. It is not headless in the way that it offers all the functionality of Drupal without using Drupals front-end. For example, these words I am typing is not typed into a decoupled web-app or command-line tool. Its only headless feature is that it loads content pages with ajax through Drupal 8's new REST module. Let's look at a typical set-up for this, and how I approached it differently.
A typical setup
A common way to build a front-end JavaScript application leveraging a REST API, is using a framework of your choice (backbone / angular / or something else *.js) and build a single-page application (or SPA for short). Basically this could mean that you have an index.html file with some JavaScript and stylesheets, and all content is loaded with AJAX. This also means that if you request the site without JavaScript enabled, then you would just see an empty page (except of course if you have some way of scraping the dynamic content and outputting plain HTML as fallback).
Head fallback
I guess the "headless" metaphor sounds strange when I change it around to talk about "head fallback". But what I mean with this is that I want a user to be able to read all pages with no JavaScript enabled, and I want Drupal (the head) to handle this. All URLs should also contain (more or less) the same content if you are browsing with JavaScript or without it. Luckily, making HTML is something Drupal always has done, so let's start there.
Now, this first part should be obvious. If a user comes to the site, we show only the output of each URL as intended with the activated theme. This is a out-of-the box feature with Drupal (and any other CMS). OK, so the fallback is covered. The next step is to leverage the REST module, and load content async with AJAX.
Head first, headless later
A typical scenario would be that for the front page I would want to request the "/node" resource with the header "Accept:application/hal+json" to get a list of nodes. Then I would want to display these in the same way the theme displays it statically on a page load. The usual way of doing this is that when the document is ready, we request the resource and build and render the page, client side. This is impractical in one way: You are waiting to load the entire document to actually render anything at all. Or maybe even worse: You could be waiting for the entire /node list to load, only to destroy the DOM elements with the newly fetched and rendered JSON. This is bad for several reasons, but one concrete example is a smart phone on a slow network. This client could start rendering your page on the first chunk of html transferred, and that would maybe be enough to show what is called the "above the fold content". This is also something that is a criteria in the often used Google PageSpeed. Meaning in theory that our page would get slower (on first page load) by building a SPA on top of the fallback head.
It is very hip with some "headless Drupal" goodness, but not at the cost of performance and speed. So what I do for the first page load, is trust Drupal to do the rendering, and then initializing the JavaScript framework (Mithril.js in my case) when I need it. Let's take for example you, dear visitor, reading this right now. You probably came to this site via a direct link. Now, why would I need to set up all client side routes and re-render this node when all you probably wanted to do, was to read this article?
Results and side-effects
OK, so now I have a fallback for JavaScript that gives me this result (first picture is without JavaScript, second is with JavaScript):
As you can see, the only difference is that the disqus comment count can not be shown on the non-js version. So the result is that I have a consistent style for both js and non-js visitors, and I only initialize the headless part of the site when it is needed.
A fun (and useful) side-effect is the page speed. Measured in Google PageSpeed this now gives me a score of 99 (with the only suggestion to increase the cache lifetime of the google analytics js)
Is it really headless, then?
Yes and no. Given that you request my site with JavaScript enabled, the first page request is a regular Drupal page render. But after that, if you choose to go to the front page or any other articles, all content is fetched with AJAX and rendered client side.
Takeaways and lessons learned
I guess some of these are more obvious than others.
- Do not punish your visitor for having JavaScript disabled. Make all pages available for all users. Mobile first is one thing, but you could also consider no-js first. Or both?
- Do not punish your visitor for having JavaScript enabled. If you render the page based on a AJAX request, the time between initial page load and actual render time will be longer, and this is especially bad for mobile.
- Subsequent pages are way faster to load with AJAX, both for mobile and desktop. You really don't need to download more than the content (that is, the text) of the page you are requesting, when the client already have the assets and wrapper content loaded in the browser.
Disclaimers
First: these techniques might not always be appropriate for everyone. You should obviously consider the use case before using a similar approach
If you, after reading this article, find yourself turning off JavaScript to see what the page looks like, then you might notice that there are no stylesheets any more. Let me just point out that this would not be the case if your _first_ page request were without JavaScript. By requesting and rendering the first page with JavaScript, your subsequent requests will say to my server that you have JavaScript enabled, and thus I also assume you have stored the css in localStorage (as the js does). Please see this article for more information
Let's just sum this up with this bad taste gif in the category "speed":
ManuelBS•Monday, Nov 24th 2014 (almost 10 years ago)
Thanks for the nice post. I red many articles about headless Drupal but this on seams to be the only one that cares about no JS users. For content sites this is very important, not only for seo reasons. For business applications the focus is a little bit different but having a headless approach with a no JS fallback is always a plus for fast apps that use Drupal as a backend.
adam balsam•Monday, Nov 24th 2014 (almost 10 years ago)
Nice write up. This method does seem to mess with the browser's history however. Starting with a direct link to this post, click on the "Please see this article for more information" link above. Then click your browsers back button. You remain on the "Now running Drupal 8, in the most hipster way imagined." post instead of returning to this one.
eiriksm•Monday, Nov 24th 2014 (almost 10 years ago)
Hey you are right. I never noticed. But that is more of a bug in my implementation than the tactic itself. Thanks for reading and commenting!
(edit: ...and fixed. Relative URL vs absolute URL problem :))
adam balsam•Monday, Nov 24th 2014 (almost 10 years ago)
Nice!
Wim Leers•Monday, Nov 24th 2014 (almost 10 years ago)
This is basically PJAX. See http://yuilibrary.com/yui/d... and https://www.drupal.org/proj....
P.S.: I can't sign in with Disqus. Not this morning (>12 hours ago), not now. Drupal comments wouldn't have this problem :)
eiriksm•Tuesday, Nov 25th 2014 (almost 10 years ago)
Hello. Thanks for commenting.
This is a good point, although I do not 100% agree.
First, let me just say the intention with this article was not say I invented some new technique. It is mostly about having a fallback, and secondly about using Drupal as your index.html (or so to speak).
Another intention of this article was to point out that people doing "headless Drupal" do not necessarily need to bootstrap their client side app (which is more likely to be angular / backbone / *.js and not something with pjax) before they have to. Which is what I do. So I did not invent some fancy technique, I just bootstrap the app a little later, and that app and framework is still doing all the work for me.
That being said, I don't fully agree this is the same thing as pjax. The end result is very similar for the user, but backend-wise this is using the JSON representation of the node when AJAXing, and using the HTML representation of it otherwise. I think that is nice, semantically speaking.
Closing, I would also like to say thanks to you for all your great work on Drupal core. I have seen your name come up on issues I have encountered more than once ;)
(PS. The disqus thing was weird. I actually signed in both yesterday morning and about 12 hours later (in my TZ at least), at work and on my phone. So no idea what's going on there. But Drupal comments is not coming back on my site any time soon ;))
Wim Leers•Thursday, Nov 27th 2014 (almost 10 years ago)
You're absolutely right. I was just looking at this from a front-end performance POV, in which case it's equivalent with PJAX.
Thanks for experimenting with headless Drupal — please keep posting interesting articles like this one :)
I think it's awesome that you take into account front-end performance, many front-end folks forget that nowadays, and only test their sites on low-latency, high-bandwidth connections, and hence such sites are extremely slow (and often even brittle/broken) on mobile devices with high-latency connections…
Keep up the good work! :)
eiriksm•Thursday, Nov 27th 2014 (almost 10 years ago)
Hey, thanks for the kind words! I agree 100% on the front-end performance point. It is more often overlooked than other parts of the optimization of a site. Performance is king, and it goes all the way from reverse proxy caching strategy to bloated css and js.
Thanks again for commenting!
...and now you actually got signed in to Disqus! :)
Wim Leers•Thursday, Nov 27th 2014 (almost 10 years ago)
Yep, Disqus is up & running again.
Out of curiosity: what do you like so much about Disqus that you're willing to put up with its unreliability, it owning your data, it (soon) advertising on your site over Drupal's comments? Anything in particular?
The thing I personally miss most is signing in with Twitter to comment.
eiriksm•Friday, Nov 28th 2014 (almost 10 years ago)
A bit off-topic here, but interesting subject though.
First of all, I don't like _dealing_ with comments. Even with all kinds of filters and honeypots there is _always_ spam in Drupal comments (or not particularly Drupal, just your own comments), unless you actually include some form of CAPTCHA, which is something I intensely dislike. In addition to the obvious reasons, for disliking CAPTCHA, I don't like the absurdity of it. Imagine telling someone from 1950 that, "yeah, to voice my opinion about something I have to try to read something almost unreadable to prove I am not a computer".
Second, I like the fact that commenting will not invalidate my page cache.
Other than that, I only see negatives with it (for example like you point out that they are owning my data.) But ads? I will actually switch back if there is ads showing up.
So I guess the answer is, right now it is the lesser evil, for sanity and ease of maintenance.