How to setup a PWA with Quasar 2 and Laravel 9

Taking full advantages of both frameworks while maximizing the separation of concerns.

This is an updated version of this article, head there if you need the guide for Quasar 1 and Laravel 6.

Here at Dreamonkey, we recently had to develop a PWA (progressive web app). Since the Quasar framework is well suited for such a task, we immediately decided to use it to develop its frontend app. However, we wanted to use Laravel as the backend framework due to its maturity and many features, so we had to find a way to make both environments work together.

And I know what you’re thinking: “Why another article on how to stitch these frameworks together?”
Well, the answer is simple: we were not completely satisfied with the solutions those articles propose. We will also give you some advices about the development environment fine tuning.

Installation and setup with separation of concerns

Our first objective was to keep the backend and frontend environments as much separated as possible but without having to manage two different repositories. In this way, if we need to replace either framework with a new one, they are easily separable. However, in order to use Laravel as the backend, the whole Quasar built app has to be served from within Laravel’s public folder.
Therefore, we decided to have two folders inside the Git project’s root: backend for holding all Laravel relevant files and frontend for installing Quasar. In order to avoid manually copying Quasar files each time a new version of the frontend has been built for production, we made use of a particular hook of the quasar.config.js.
Let’s have a look at how we did all of this.

First of all, we highly recommend you to install Laravel Homestead. It provides a virtual machine equipped with a convenient environment ready to host Laravel applications without polluting your main OS environment.
Once Homestead has been installed, configure it to synchronize your project root folder (eg my-project). Then connect to the Homestead machine via SSH and move into your project folder:

$ cd my-project
$ composer create-project laravel/laravel backend

Then we need to set up Quasar. This requires you to have installed Node.js on your main OS (not on your Homestead virtual machine) to achieve a better performance during development with HMR. In addition, we always recommend the usage of pnpm or Yarn 1 as package managers for Node.js.
Open a terminal into your project folder root and run the following command:

$ pnpm create quasar

When prompted, specify frontend as the project folder name and others options as you prefer.

Next, we need to inform Quasar that once the code for distribution of our app has been built, it must be transferred to the public folder so that Laravel can serve it. Open the Quasar project you have just created in your favourite IDE and open the file quasar.config.js, where you have to look for the build object and add this function as one of its methods:

afterBuild({ quasarConf }) {
  if (ctx.mode.spa || ctx.mode.pwa) {
    const composeDistPath = src => path.join(quasarConf.build.distDir, src);

    const composeServerPath = src =>
      path.join(__dirname,'../backend/public', src);

    for (const fileName of fse.readdirSync(quasarConf.build.distDir)) {
      fse.removeSync(composeServerPath(fileName));
      fse.copySync(composeDistPath(fileName), composeServerPath(fileName));
    }
  }
}

Note that path is the native Node module and fse is fs-extra, you should install the latter in your project and import them both as const path = require('path') and const fse = require('fs-extra') at the top of your quasar.config.js.

We also suggest using Quasar routing in hash mode, otherwise you will have to adapt Laravel routing to support VueRouter history mode.

Since Laravel uses only one entry point for serving the application, we must create a Blade view which includes the index.html file used by Quasar to mount the application:

@php
    // This file must be stored inside Laravel resources/views/app.blade.php
    // this include_once resolves the import from Laravel public folder
    @include_once 'index.html';
@endphp
<?php
// Inside the routes/web.php file configure the new view to be served for all page requests
Route::view(‘/’, ‘app’);

Now that the basics are in place, if you run $ quasar build from inside the Quasar folder terminal and then try to connect to your Laravel website, you should see the default Quasar app.

Making Laravel accept REST APIs calls from Quasar

To provide security and prevent fraudulent access to the application, Laravel uses a system of tokens to verify and authenticate requests made to its APIs. We have to configure those into both our frontend and backend if the app needs to be able to make API calls.

The first thing to do is to enable and configure Sanctum, a library which adds to Laravel the capability of handling requests coming from SPAs.
Then we must configure the stateful domains from which our fontend will make requests. In your project you should make sure that the following options have been set like this:

# .env
APP_DOMAIN=your-website.com
APP_URL=http://${APP_DOMAIN}
SESSION_DOMAIN=.${APP_DOMAIN}
SANCTUM_STATEFUL_DOMAINS=${APP_DOMAIN},localhost:8080
// Into config/cors.php
'supports_credentials' => true,

The . used inside the session domain is mandatory to handle domains correctly. CORS configuration is the most important and tricky part to get right, and we invite you to watch this video, where many possible issues are addressed.\

Now you should add all routes which should only be be accessible by authenticated users into a route group protected by auth:sanctum middleware.

That takes care of the backend, but there are some tweaks to be made on the frontend too.
By default, Quasar ships with Axios as a library for AJAX requests and Laravel recommends its usage too, so that’s where we’re gonna do most of our configuration.

All AJAX requests must set the property withCredentials to true otherwise Laravel will reject them.
In addition, before making a request, the application must hit a special endpoint called /sanctum/csrf-cookie, which will set a CSRF cookie token into the application. Since the token has an expiration date, a good way to avoid requests suddenly failing is to request a CSRF token before each API call to ensure that the token will be renewed.

To accomplish these two tasks with Axios, copy over the following code at the top of your axios boot file

axios.defaults.withCredentials = true;
axios.interceptors.request.use(async function (config) {
  if (!config.url.includes('/sanctum/csrf-cookie')) {
    await axios.get('/sanctum/csrf-cookie');
  }
  return config;
});

This setup works if each time we edit the frontend we then trigger a Quasar build, but developing like this is slow. To take advantage of HMR, we need once again to edit the quasar.config.js file, this time configuring devServer option:

module.exports = configure(function (ctx) {
  return {
    devServer: {
      server: {
        type: 'http',
      },
      port: 8080,
      open: true, // opens browser window automatically
      proxy: [
        {
          context: ['/sanctum', '/login', '/logout', '/api'],
          target: 'http://192.168.56.56', // Laravel Homestead end-point
          // avoid problems with session and XSRF cookies
          // When using capacitor, use the IP of the dev server streaming the app
          // For SPA and PWA use localhost, given that the app is streamed on that host
          // xxx address is your machine current IP address
          cookieDomainRewrite:
            ctx.modeName === 'capacitor' ? 'xxx.xxx.xxx.xxx' : 'localhost',
        },
      ],
    },
  };
});

By configuring which routes and requests the dev server must proxy back to the Laravel Homestead machine, we are able to use our main OS for developing with Node.js and Quasar.

Use HTTPS during development on your local machine

The last thing to look out for while developing PWAs is to remember that they must be served over https. Recent Homestead machines automatically create a local certificate but you will have to force your browser to trust it. Unfortunately, each browser and OS have their own method to achieve this, so here there are a few guides for Linux and MacOS and a more specific one for Windows.

Start developing!

And these are the basic common configurations needed in order to start developing a PWA with Quasar and Laravel.
If you want to share your suggestions with us or you think that we got something wrong, do not hesitate to contact us!