Making axios work with external resources in Laravel Spark apps

In MemberScore, we make use of a WordPress plugin to connect a user’s site to MemberScore. The easiest way to provide that plugin is to give users a zip file that they can upload through their WordPress admin area. I could ship a zip file with my Laravel application, but since I’m already hosting that plugin’s code on Github, it seems silly to also add it to the Laravel application, and to update the file each time I make a change to the plugin code.

Github does give you a /latest release URL that redirects to the most recent release of a repository, but that would still require users to then click on the correct download link – plus it’s an extra step.

Instead, I opted to make use of the Github API, which returns that latest release as a json object: https://api.github.com/repos/tnorthcutt/member-score-wp-plugin/releases/latest. I’m then able to grab the first element of the assets array in that object, and use the browser_download_url. In my Vue component:

You’ll notice there that I’m fetching the Github URL with axios. By default, Laravel Spark makes use of axios, a handy dandy “promise based HTTP client for the browser and node.js.” Axios is very convenient to use. However, Spark also sets some default headers on requests sent with axios:

As a result, you may run into some issues using axios for external requests (outside of your application). Here’s a Laracasts thread explaining just that problem. This was the case for me; performing that request resulted in this nasty error message:

Access to XMLHttpRequest at 'https://api.github.com/repos/tnorthcutt/member-score-wp-plugin/releases/latest' from origin 'https://app.memberscore.io' has been blocked by CORS policy: Request header field X-CSRF-TOKEN is not allowed by Access-Control-Allow-Headers in preflight response.

The solution turns out to be deleting the X-CSRF-TOKEN header just before making the request, then adding it back afterwards (so we don’t break things for other parts of the application):

While this seems like a bit of a messy workaround, it does indeed work. There is currently a pull request on the axios repository to allow setting a header to null on an individual request and not sending that header as a result; that would be far preferable. As of this writing, axios still sends an empty header if you set it to null, which breaks Access-Control-Allow-Headers.

Converting Laravel Spark navigation to Tailwind CSS

I recently converted the navigation of my Laravel Spark based app, MemberScore, to use Tailwind CSS. Spark uses Bootstrap out of the box, which I wanted to move away from for two reasons:

  1. I find it a bit clunky and restricting, personally
  2. I’d built a sales site for MemberScore (at memberscore.io), and I wanted the actual app (at app.memberscore.io) to match the sales site appearance.

I haven’t converted the entire app to use Tailwind (yet?), but the nav is now converted and fully functional.

Here’s how I did it

Essentially, we’re going to recreate all the nav-related Spark views, and swap out the various css classes to take advantage of Tailwind, removing Bootstrap-related ones and instead using Tailwind classes to get the nav looking how we want.

The first step is to install Tailwind. If you’re not sure whether you’ll want to make this change permanently, I highly recommend including Tailwind via CDN as referenced in the docs, rather than going through the relative hassle of including it via npm.

If you’re ready to commit, you’ll need to include tailwindcss in your package.json file. I also needed to bump the version of laravel-mix I was loading, from ^0.12.0 to ^2.0. I also included laravel-mix-tailwind.

In my webpack.mix.js file, I was then able to follow the instructions provided by laravel-mix-tailwind, and get Tailwind compiling ok after adding the tailwind directives to the end of my app.less file:

You’ll notice the bootstrap-overrides.less file there; I had to make a couple of additions to override some default Bootstrap files that didn’t play well with Tailwind:

That may not be the Right Way™ to do this, but it’s working ok for me.

Next, I had to figure out how to get two things working:

  1. The toggling of the main nav menu on small screens
  2. The account switcher/settings/kiosk etc. dropdown in the nav

I ended up creating a single file Vue template called MainNav.vue and using it as an inline template; this let me still use blade helpers within it. Here’s the template:

And where it’s used in resources/views/nav/user.blade.php:

Toggling visibility

Notice the navOpen and dropdownOpen props, and associated toggleNav() and toggleDropdown() methods; those handle showing/hiding the two items mentioned previously. You can see there’s a button that calls toggleNav(), and the main div containing the nav items applies a class, either block or hidden, depending on the current value of navOpen (which is what toggleNav() alters).

The dropdown toggling is done exactly the same; you can see dropdown-toggle.blade.php is included in the main nav file. For some reason, I ended up with dropdown.blade.php being @include()-ed in dropdown-toggle.blade.php instead other way around, but the names don’t really matter.

Wrap up

That’s the general gist of how I did this. I’m sure this isn’t all done as perfectly as it could be, but it’s functional, looks how I want it, and I understand it – and those three things go a long way in production.

If you have any questions, or suggestions, shoot me an email at travis@travisnorthcutt.com. I’d love to hear from you.

Git clean

Thanks to Josh Eaton, I recently discovered git clean. This command came up because I had somehow created a rogue file in the root directory of a site I was working on (I’m not even sure how it got there), but couldn’t figure out how to delete it, due to its odd name:
Bizarre filenameJosh helpfully suggested that I try using git clean, which worked!

Basically, git clean removes any “untracked files” from the working tree. That is, it removes any files you haven’t already told Git about (e.g. via git add [files]). This was useful in my case because I couldn’t easily delete the file otherwise, but it could also come in handy if you started experimenting with some new work, especially if that work were in several different new files, that you hadn’t already started tracking yet.

Warning: this will delete files, so be careful! In my case, just running git clean by itself did nothing, resulting in the error message

fatal: clean.requireForce defaults to true and neither -i, -n nor -f given; refusing to clean

However, just to be sure, you should always run git clean --dry-run first, which will show you what would be removed if you ran it with the -f or –force flag. You can also perform an interactive clean; see the linked documentation for more information.

Mobile First Device Detection

I think Luke Wroblewski’s approach to responsive web design, which he calls RESS (Responsive Web Design + Server Side Components) is a pretty solid concept. I”ll let him explain:

In a nutshell, RESS combines adaptive layouts with server side component (not full page) optimization. So a single set of page templates define an entire Web site for all devices but key components within that site have device-class specific implementations that are rendered server side.

The idea is that instead of serving every device the exact same content the exact same way, some things are served differently depending on the device class (not specific device!).

Luke ends his post with a note that relying on user agent detection can be unreliable at times, and might be an issue in some situations:

That said, there may be a few unsolved challenges.

Relying on user agent detection on the server to decide which device class specific components to include could be an issue for some. There’s a lot of debate about how accurate user agent detection is.

This is a growing rather than shrinking issue; seemingly every day a new mobile device is launched, and keeping up with all the various user agent strings is difficult bordering on impossible.

However, what if, instead of trying to ferret out whether the connecting device is one of hundreds of possible mobile device/browser combinations, we instead attempt to discover if it’s one of a few known desktop browsers, and then beyond that try to separate out large (iPad & other tablets) and small (phone-size) “mobile” devices? This is the approach that Categorizr takes, and I think it has a lot of potential.

What makes Categorizr different?

Instead of assuming the device is a desktop, and detecting mobile and tablet device user agents, Categorizr is a mobile first based device detection. It assumes the device is mobile and sets up checks to see if it’s a desktop or tablet. Desktops are fairly easy to detect, the user agents are known, and are not changing anytime soon. Tablets are also pretty easy to detect.

By assuming devices are mobile from the beginning, Categorizr aims to be more future friendly. When new phones come out, you don’t need to worry if their new user agent is in your device detection script since devices are assumed mobile from the start.

I think this approach allows you to avoid the common pitfalls of responsive web design. Obviously it’s not a panacea (nothing is), but it has potential to be a solid technique.

What do you think is missing with this method? Are there situations or contexts that it overlooks or glosses over?