A couple of years ago, I finally decided to dive head-first into some of the newer JavaScript technologies that I've been putting off for a while, such as Webpack, Vue, and NodeJS.
Before I was "enlightened", when I wanted to include JS or CSS in my regular ol' web app (built with Laravel), I would just include it in a script
or link
tag in my HTML. Pretty simple. And, I was perfectly content doing this. I even used Gulp a little bit, mostly for minifying my own JS and compiling Sass stylesheets. But, it was becoming clear that there was this huge ecosystem of things happening in JS-land and I was feeling at risk of falling behind if I didn't at least try to learn some of it.
So, I incorporated some Vue into my existing project, without Webpack or build tools of any kind. I really enjoyed how Vue handled reactivity and DOM updates. Compared to jQuery, it was like magic. I was able to throw away so much spaghetti jQuery on a page and basically create a full-blown mini-app with just a simple Vue instance.
After that great experience, I started looking into how Vue is used out in the wild. While my particular use-case as a simple add-in to a traditional server-rendered web app was actually pretty common, it became evident that Vue is really built with single-page apps in mind. This led me down a rabbit hole of Webpack, Node, single-page app architecture, NPM, new authentication schemes, new data-fetching schemes (REST, GraphQL)…
Despite having to learn all these new things, I decided to do a rewrite of my Laravel side project in pure JavaScript using AdonisJS and Mithril after playing around with them a bit. And, I learned a great deal from that experience. I built a front-end single-page app in Mithril, backed by an Adonis API. This allowed me to create a really interactive experience in the front-end, and made the app feel more like an "app" rather than a "website". It did, however, present a lot of new challenges.
Secure authentication
Authentication is actually not very straightforward to implement, if I truly separate my API from the user-facing app. I have to be very careful how I store and use auth tokens (such as with OAuth and JWT), and I have to be conscious of more threats than with session-based backends protected by CSRF. In other words, there are a lot of ways to mess it up.
Efficiently fetching data
With a single-page app, usually it would fetch data from an API, or multiple APIs. However, I have to be careful to only grab the data I need, when I need it. I would often grab too much and have more data than I need, or, I would grab too little and have to make multiple, inefficient requests. Solutions like GraphQL are meant to address this, but it adds another layer of concern to both the front and back-end.
Decoupled architecture
While this can be very beneficial if I want to offer a suite of end-user products (web, mobile, desktop, toaster, etc.), it does make it more expensive early in the project to basically have to build a minimum of two products in order to get to release. This makes distribution a bit more complicated, and can be a bottleneck early on when I really want to be moving as fast as possible.
Testing
I've only recently really embraced test-driven development when building my own web apps, but now I have to write tests for two apps (single-page app, API) instead of just one. End-to-end tests are easy on the API, but I have to always make sure the test mocks on the front-end match my API, assuming I want to test the SPA and API independently. A change in one of the apps usually means I have to change the other one as well.
With a "traditional" web app, all the unit and end-to-end tests can be included in the same test suite.
Final thoughts
While I did learn a ton creating an API/single-page app architecture (and likely will again in the near future), I've decided to go back to doing things "old school", but with some "new school" sprinkled in for my current project. I'm doing a V3 rewrite as a Laravel app again, without an API. You could say I've embraced the Majestic Monolith.
However, the templates for the app will actually be a single-page app thanks to InertiaJS, an innovative library that allows me to write my templates in Vue, React, or Svelte without having to use an API. I'm literally getting the best of both worlds; the stability and simplicity of a traditional Laravel app, combined with the cutting-edge, interactive UI of a single-page app. Perhaps I'll write more in-depth about Inertia in the future, but in the meantime, you should check it out!
Top comments (3)
Heard about InertiaJS, really an interesting and innovative concept. I'm busy building an "old school" app as well, using jQuery in the frontend, and I wonder if for "release 2" I should switch the architecture to get a bit more of an "app-y" feel. Thought about Turbolinks with StimulusJS, but also about going 'all the way' with API +SPA (but then using GatsbyJS to build the frontend). InertiaJS seems an interesting third alternative.
Yeah I looked into Turbolinks too, but the hesitation I had with it was I have a lot of JavaScript libraries that would conflict with Turbolinks. But, since I'm just building a Vue SPA on the front-end, it's pure JavaScript and it works great!
You're right, Turbolinks works great if you do everything "the Turbolinks way" but that means you can't just drop in any 3rd party lib and expect it to work. InertiaJS sounds really magical, it's a genius concept.