Tired of JavaScript Fatigue? HTMX Revives RESTful APIs
Net API Notes for 2024/04/04, Issue 235
Regarding APIs, REST purists are always quick to point out that "JSON over HTTP" doesn't meet the REST architectural style expectations as defined by Roy Fielding. Most often, these "RESTful" or "RESTish" APIs do not fulfill the hypermedia as a medium of exchange with the server property. That is, rather than just JSON objects ping-ponging back and forth between the server, true REST APIs should also contain actions the client can use to "follow their nose".
HTMX is a dependency-free JavaScript library that attempts to provide modern-day web application behavior while encouraging proper REST API communication with the backend. In this edition of Net API Notes, I will define what JavaScript fatigue is, revisit the power of hypertext in your hypermedia, and demonstrate how HTMX brings those together for select use cases.
That, and more, in this edition of Net API Notes.
Introducing HTMX
HTMX was developed by Carson Gross, principal at Big Sky Software and a computer science instructor at Montana State University, in Bozeman. He also authors grugbrain.dev, a website of software development essays authored from the perspective of a cave man. Gross previously created intercooler.js, a jQuery library to easily add AJAX to HTML attributes. According to his retelling, HTMX began as a project to keep himself busy during COVID-19, seeing if he could remove the other JavaScript library dependencies.
The concept behind HTMX is that rather rely on JavaScript for modern application development, could hypermedia:
- Allow any element to issue a request, rather than just links and forms?
- Allow any event to trigger that request, as opposed to only clicks and submits?
- Surface PUT, PATCH, and other HTTP actions in vanilla HTML?
- Not replace the whole screen on every request?
The resulting HTMX is a lightweight JavaScript library that encourages applications to leverage the simplicity and power of hypertext. As Gross describes it, HTMX is a "return to hypermedia" and a "cure for your javascript fatigue".
What SPA Hath Wrought
JavaScript fatigue refers to the explosion of available libraries and framework technologies, which makes it difficult to keep up with not only the latest library, but even the latest version of a utilized thing.
Gross attributes JavaScript fatigue to working in a (relatively) young field. But he's also not wrong in pointing out that it is caused, at least in part, by cultural factors, where clout comes with speaking and using the next, newest thing. The result is an increasing cost of Single-Page Application (SPA) complexity that begins to outweigh the provided abstraction benefits.
In his "3rd Way Web Development" series, Michael Carducci has a fantastically detailed history of how we've gotten to this point. I agree with Michael that AJAX was a critical stepping-stone in the history of the web - it facilitated new, more interactive experiences. However, pursuing this path meant that, for example, the anchor tag ("<a href=") became less about being a hypermedia control and more of a user interface element.
By reducing HTML to only a user-interface markup language, several interaction patterns provided by the browser are either lost or have to be recreated by other means. When portions of the page are dynamically loaded, pressing the back button doesn't undo the last paint, but the last page - something libraries had to code around. Similarly, sharing a link rarely loads the correct application state - again, something libraries broke that had to be reinvented. HTML, which was once sufficient to allow a generic client, the browser, a functional experience was now reduced only that which was necessary to bootstrap a chain of JavaScript calls.
"I personally saw one application rewritten four times in six years (ext.js -> jQuery -> knockout -> angularJS) and worked with many applications that ultimately ended up using most (or all) of these simultaneously as trends would typically shift mid-rewrite. Of course, at this point, not only did the application need to be changed but so, too, did the entire toolchain. Gulp, Grunt, Webpack, SASS, LESS, Babel, Bazel… The list goes on and on. New framework versions often introduced some number of breaking changes, and an increasing amount of most organization’s development budget shifted from new features and innovation to simply keeping up with trends and applying the latest toolchain and runtime additions to fix what always existed until the latest framework “innovation.” Sections of the app dependent on an old version of a library or framework; or built using an abandoned toolchain or library we relegated to a growing mountain of tech debt." - Michael Carducci
Sadly, it is all too reminiscent of this YouTube "Interview with Senior JS Developer":
The introduction of JavaScript, quickly followed by AJAX, allowed calls to happen outside the regular hypermedia request and response. This was initially great for interactivity. However, the "SPA frameworks" grew in proportion to the complexity they were expected to manage. In the process, a hypermedia-centric view of the world was replaced by a JavaScript-centric one. That process established RPC-style behavior as the dominant interaction style in front-end development.
A Quick Example of Hypertext's Advantages
An example to illustrate what I'm referring to would be helpful here. In his presentation, Solving Javascript Fatigue Using Fundamental Web Architecture, Gross shares the following two examples: the first in hypermedia and the second in JSON.
Both of these examples represent a bank account in good standing. However, Gross correctly points out that an underappreciated aspect of Roy's dissertation is the idea of a "uniform interface". When given the first example, a browser doesn't have to understand the concept of a "bank account"; it only has to be able to display the HTML. The client is de-coupled from the backend model. Hypermedia encodes the balance AND includes the actions available on the account.
However, in the JSON example, the thick client has to understand not only what a bank account is but also the relevant actions that could be allowed on such a service. JSON is "leaner" - it only provides a "status" property. But it becomes the client's responsibility to interpret this information and provide the appropriate actions.
Let me jump forward to Gross's update to the example to illustrate this point:
In this case, the bank account is overdrawn. The JSON response is largely the same; while the status and value fields have changed, it is still up to the client to understand not only the bank account concept but also the actions that can occur when in this state.
In the hypermedia response, the server simply renders a different set of links. This "uniform interface" means that hypermedia can change dramatically, updating features and functionality without breaking the client application.
Hoping to better appreciate a "non-JSON API" approach, I spent some time working on a sample application. Some readers might remember my API Job Listing analysis from January. I worked with that data primarily through queries run directly in the pgAdmin console and spreadsheet dumps. I thought creating a basic search, sort, and annotation tool for easier and more granular data work would be a useful trial of HTMX.
The results were enlightening. One of the biggest challenges was unlearning years' worth of "separation of concerns" assumptions; that is, not just treating the response to an API call as data retrieval but something that also contained the allowed actions. Part of this includes rethinking how to maintain separation of concerns in the HTML.
For example, consider the 'zebra striping' on the table in the background. Am I putting specialized classes in each row of the hypermedia response to achieve the effect? (Bad) Or do I need to limit client display to CSS to achieve the same result [tr:nth-child(even) and tr:nth-child(odd)]. (Good).
That was an adjustment. However, with a bit of practice and some course correcting, things quickly snapped into place.
On the Other Hand…
Thus far, I've hopefully made a compelling argument for the advantages of using hypermedia in a thin client. However, when we start talking about native mobile applications or server-to-server communication, the benefits get less obvious.
Native mobile applications can display hypermedia within an embedded browser. However, these renderings often appear clunky or odd when compared with polished interactions crafted directly in Swift or Kotlin. In those cases, the thick clients need to be more aware of the model; they need to understand what a bank account is.
When talking about machine-to-machine communication, there isn't a human with the smarts to "follow one's nose". A computer attempting to perform a 'transfer', as in the bank account example, would have to have a semantic understanding of each action listed in the hypermedia response.
As shown by the large number of API hypermedia formats (HAL, JSON-LD, ALPs, Siren, Mason, etc.), developers have endeavored to provide these affordances in various ways. However, nearly all of them involve some grafting of semantic meaning over JSON objects. This lack of a "uniform interface" has hindered their adoption.
Conclusion
All in all, I find HTMX a powerful replacement for places where I might normally consider a single-page application. It shines in places where I want "modern" applications reactivity while minimizing synchronization between my client and my server models. However, for teams attempting to create the one API for multiple experience layers (web, mobile native, and - I dunno - voice assistants?) it isn't a silver bullet.
Finally, I'd be remiss if I didn't give a shout-out to Jon Moore. During his long tenure at Comcast, he was an early (like 2014/2015-ish?) and vocal proponent of returning HTML from APIs for precisely this reason. It just goes to show that good ideas never really die; they just cycle through the zeitgeist.
Milestones
- Meta recently demonstrated Thread's ActivityPub integration. This was rapidly followed by the President of the United States creating a Thread's account, followable from any other account in the Fediverse, Meta or not. This is apparently an intermediate step, with the White House looking into running their own instance (pending figuring out the federal policy and procedures part). Relatedly, Gitlab is considering using ActivityPub to create a "global Gitlab network". This may be the critical mass for the protocol I hypothesized about in 2022.
- Spectral has a new OWASP API Security Ruleset to add to your OpenAPI description automation.
- Tyk released an API Platform Maturity Model. Which is… fine, I guess? I find I've soured on the effectiveness of (many) maturity models lately, unless they're from maturity-model.online. Now, those are great.
- This past month, I saw that Ron Ratovsky was looking for their next opportunity. If you're looking for experienced help with APIs, developer tooling, or developer experience, give Ron a direct message on LinkedIn.
Wrapping Up
If you'd like to support Net API Notes, head over to the subscription page. A monthly or yearly pledge helps Net API Notes remain ad-free and ensures previous commentary, analysis, and insights remain available for all.
That's all for now. Till next time,
Matthew (@matthew in the fediverse and matthewreinbold.com on the web)