How to setup a PWA with Quasar and Laravel

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

This article has been written with Quasar 1 and Laravel 6 in mind, head here for the updated version using guide for Quasar 2 and Laravel 9.

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.conf.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 so that your files will be copied into the virtual machine. Then from the Homestead command line run:

$ cd yourVirtualMachine/pathToHomesteadSharedFolder/yourProjectRootName
$ composer create-project --prefer-dist laravel/laravel yourProjectName

This will create a Laravel project with the name set to yourProjectName. Once the project has been created, rename the yourProjectName folder to backend.

Then we need to set up Quasar. This requires you to have installed both Node.js and Quasar CLI 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 Yarn as a package manager for Node.js.
Open a terminal and run the following commands:

$ cd yourMainOs/pathTo/yourProjectRootName
$ quasar create yourProjectName

Once the installation has been completed, rename the yourProjectName folder to frontend.

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.conf.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.conf.js.

We also suggest using Quasar routing in hash mode, otherwise you will have to configure all routes also inside Laravel.

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/your_name.blade.php
    // this include once makes reference to the public folder by default
    @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('/', 'your_name');

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 install Sanctum, a library which adds to Laravel the capability of handling requests coming from SPAs, and remember to add the middleware in the group, otherwise APIs will not work.
Then we must configure the stateful domains from which our fontend will make request. 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}
LOG_CHANNEL=daily
SESSION_DOMAIN=.${APP_DOMAIN}
SESSION_LIFETIME=120
SESSION_DRIVER=database
SANCTUM_STATEFUL_DOMAINS=${APP_DOMAIN},localhost:8080
// 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.
The session lifetime is important to determine after how much time in minutes the tokens will be valid before they must be renewed.
In addition, we suggest using the session driver database, since it is more secure for more complex scenarios, even if it requires a little bit more configuration, but for simple use cases also the file driver can be used.

That takes care of the backend, but there are some tweaks to be made also on the frontend.
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. By default, Quasar ships with Axios as a library for AJAX requests, and Laravel recommends its usage too, so the token fetching can be achieved by using Axios interceptors. However, we preferred to write a custom wrapper around the JavaScript fetch function and use it to perform API calls.

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.conf.js file:

module.exports = configure(function (ctx) {
  return {
    // Other configure options
    // Full list of options: https://quasar.dev/quasar-cli/quasar-conf-js#Property%3A-devServer
    devServer: {
      https: false,
      port: 8080,
      open: true, // opens browser window automatically
      proxy: [
        {
          context: ['/sanctum', '/login', '/password', '/logout', '/api'],
          target: 'http://192.168.10.10', // 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!