Progressive Website App Notifications with Laravel, Vue.js, and Twilio Notify
Time to read: 9 minutes
Progressive Web Applications (PWAs) are installable websites that provide an app-like experience for your users. They are made possible through technologies like Service Workers and responsive designs, which enable them to provide nearly the same user experience as native applications.
The Twilio Notify API enables you to send notifications to your users across different channels - Web, SMS, Android, and iOS - using a single API.
In this article, we will be building a recipe PWA with Laravel and Vue.js, with the ability to notify our users when a new post is available using the Twilio Notify API.
Prerequisites
To get started with this tutorial you will need the following dependencies:
- Laravel CLI, MySQL, Composer, and NPM installed on your computer
- Basic knowledge of Laravel
- Basic knowledge of Vue.js
- A Twilio Account (Create an Account for Free)
- A Firebase account (We will be using Firebase Cloud Messaging as our binding type)
Getting Started with the Sample Application
To get started, run the following command to create a new Laravel application and change into the directory:
User Model and Migration
Our recipe app requires two models - User
(which will help us tailor notifications to the user’s preferred channel) and Recipe
. Since Laravel already generated the User model for us, we only need to modify it to suit our application needs. Open the User migration file in the database/migrations
directory and update the up
method as shown below.
Also, update the $fillable
attribute of the User
model class in the app
directory as shown.
Recipe Model and Migration
Create the Recipe
model with its corresponding migration file using the -m
flag.
Next, open the newly created Recipe
model class file and replace the contents with the following.
Our recipes need to contain formatted texts, such as lists and headers. We achieve this using Mutators in the getBodyAttribute
method above. Essentially, the body string is stored in markdown and transformed into HTML using the CommonMarkConverter package which comes bundled with Laravel.
Next, update the recipes migration file located in the database/migrations
folder:.
Databases and Running our Migrations
Next, we will set up the application database. Open your .env
file and update the database credentials as followed.
Persist the changes to our database by executing the migrations.
Seeding the Application Database
We need to add some dummy data to the tables we just created to help us focus on building out the other parts of our application. Generate the needed seeders with the command below:
Open the UsersTableSeeder file (database/seeds/UsersTableSeeder.php
) and replace the content with the following:
Then replace the content of the RecipesTableSeeder file with the following:
To make our seeders discoverable to Laravel, open the database/seeds/DatabaseSeeder.php
file and update it’s run method to the code below:
Next, apply the seeders by running the command below in your terminal.
Creating our Controllers
Our application needs three controllers:
RecipeController
: responsible for managing the recipe-related routesSPAController
: to load and render our app shell before handing over to Vue Router andNotificationController
: which will handle our notification-related actions.
Create the controllers with the following commands:
Next, open the newly generated RecipeController (app/Http/Controllers/RecipeController.php
) and add the following:
Next, we register the methods in the RecipeController with our API route. Open routes/api.php
and add the following routes:
NOTE: You can read more about route groups and the prefix key above here in the Laravel documentation.
Testing Endpoints with cURL
We will be testing our API endpoints with cURL, even though the requests can be easily replicated with Postman. These tests help ensure that requests to our application are properly handled and we get the expected responses before using them in the user-facing front-end. Ensure your Laravel server is already running (you can start it with php artisan serve
) before running the commands below.
Creating a new Recipe
Run the command below to create a new Recipe via cURL.
You should get a response similar to the one below. The recipe body is also transformed into HTML due to the Eloquent mutators we implemented earlier.
Fetching all Recipes
To retrieve a JSON array of all our recipes (which corresponds to our index route), run the following command in your terminal:
Your response output should be similar to:
Fetching a single Recipe
Next, we retrieve a single recipe instance by its ID like so:
And we should get a result similar to the one below
Leveraging Vue-Laravel Integration
Now that we have a working API, let’s build out our application front-end using Vue.js. Laravel makes it easy to work with Vue.js and all the accompanying build tools out-of-the-box. To install the Vue scaffold, run the preset
Artisan and install the updated dependencies with:
Because our Vue app is coupled to the Laravel backend, we will use a Laravel web route to load the app shell, and the Vue router will handle the remaining parts. Open routes/web.php
and replace the contents with the following:
Next, open the SPAController
class file we created earlier and add the implementation of the index
method we referenced in the route above.
Note that we are re-using the default Laravel welcome
blade template here. Open the template file (at resources/views/welcome.blade.php
) and replace it with the code below:
We also need to set up Vue outer to handle our in-app navigation on the front-end. Install the vue-router
package with npm install vue-router
and update the resources/js/app.js
file to the code below:
Next, let’s create the components we will be needing. App.vue
will serve as a container for all the other components, Home.vue
will be responsible for displaying the recipe list, and Recipe.vue
will render a single recipe.
Create the file resources/js/components/App.vue
and add the following.
Similarly, create the Home component file at resources/js/components/Home.vue
and add the code below to it.
Next, create the Recipe component to show a single recipe at resources/js/components/Recipe.vue
and add the code block below in it.
Run npm run watch
in the project root folder to compile the front-end assets, and listen for modifications to those files. In a separate terminal, start the PHP server with php artisan serve
. You can now access the app at http://localhost:8000
to see what we have accomplished thus far.
Converting our Vue.js App to a PWA
Progressive Web Applications (or PWAs for short) are powered by three major components:
- A JSON-structured web app manifest file (usually named
manifest.json
ormanifest.webmanifest
) that tells the browser that our website is indeed, a PWA and installable. It also specifies application metadata and configurations such asdisplay
that tells our app how to behave after installation. - Service Workers which are actually background scripts responsible for running services that do not require interaction from our users. Some of such services are background synchronization and listening for push notifications.
- A caching strategy that helps us specify how we want our service workers to handle resources after fetching them. Such strategies include Cache-first, Cache-only, Network-first, etc. You can find more details about the different strategies in Google’s Offline Cookbook.
To inform browsers of our PWA, create a manifest.json
file in Laravel’s public
directory and add the following.
NOTE: Remember to download the app icon from here or update the icon name if you are using a custom one.
With our web app manifest in place, let’s reference it from the welcome.blade.php
. Add the following to the head
section of the template file.
Setting up the Service Worker
We will use Workbox to help us automate the process of managing our service worker. Install the workbox-cli globally with:
Once installed, launch the Workbox wizard in the project root with the command below and select the public
folder as your web root since it is the folder exposed by Laravel.
The command will generate a configuration file containing your preferred service worker file location and the files that should be cached based on your answers to the prompt.
Generate the final service worker file at public/sw.js
with the command below:
Next, we register the service worker in the welcome.blade.php
file. Add the code below in the template file just after the closing </div>
tag and right before the script tag that loads app.js
Our app should register the service worker when we visit http://localhost:8000
in the browser, though you might need to force refresh to fetch the updated asset files.
Configuring Firebase and Twilio Notify
Twilio Notify uses Firebase Cloud Messaging (FCM) as the base for its web push notifications hence, we need to create a new Firebase project on the Firebase console (or use an existing one). Copy the app credentials (Firebase Sender ID and Server Key) from your console (i.e Project Overview > Project Settings > Cloud Messaging) and add the Server Key as a Push Credential on your Twilio console’s Add a Credential Page. Set the Credential Type to FCM and paste the server key in the “FCM Secret” text box.
Also, create a new Notify service on the Twilio Console and select the push credential you just created as the FCM Credential SID. Take note of the generated Service SID as we will be using it shortly.
Next, update your project .env
file with them as in the code block below:
NOTE: The sample Firebase configuration accessible from the “Your apps” section in Project Overview > Settings > General, as it includes the credentials we will be using to use the API.
Firebase provides an NPM package that makes it easier to use all of its features in your code. Install the package with npm install firebase@^7.8.2
to ensure the version matches the one we will specify in the service worker file (i.e firebase-messaging-sw.js
). Next we will update the App
component to:
- Request for Notification permission (after the clicks the “Enable Notifications” button)
- Save the permission in Local Storage so we don’t ask again.
- Setup firebase messaging to handle notifications when our web app is in the foreground.
We achieve that by appending the code block below to App.vue
, right after the closing </template>
tag.
On initialization, Firebase looks for a publicly-accessible firebase-messaging-sw.js
file which houses the service worker as well as handles notifications when our app is in the background. Create this file in Laravel’s public
folder and add the following:
Creating Device Bindings
The Twilio PHP SDK provides a wrapper that helps us communicate with the Twilio API. In this article, that communication includes registering the user’s browser with Twilio Notify (i.e creating a device binding), as well as telling Notify when it’s time to send out notifications.
Install the SDK as a composer dependency with:
Next, we will add a public method to the NotificationsController
that accepts the user’s device token generated by Firebase and register it with our notification service on Twilio using the SDK. See how below:
NOTE: In the code above, we are using a dummy user instance. Ideally, you would want to bind the token to an authenticated user instead. If you are looking to support authentication in your Laravel/Lumen API, the Laravel Passport Docs and the Twilio Authy API are good places to start.
Broadcast new Recipes with Laravel Events
Laravel Events provide a way for us to listen for events that occur within our application. Such events include model creation, updates, and deletions. In this article, we will be using Events to send out notifications to our users when a new recipe is posted. Create the new event withphp artisan make:event RecipeEvent
and add the following code:
Register the RecipeEvent
in the EventServiceProvider
class provided by Laravel, by opening the file app/Providers/EventServiceProvider
and changing its $listen
attribute to the code below:
Next, generate the listener specified above by running:
The command should create a RecipeEventListener.php
file in the app/Listeners
directory. Open up the file and implement our event listener logic as shown below.
In the handle
method above, we are sending out the notification to all the users who have a notification_id
set, i.e users who have granted notification permissions to the app and as such, already have an identity bound to them in our Twilio Notify service.
Build the JS files with npm run prod
and start the PHP server if it is not already running with php artisan serve
. Proceed to create a new recipe via cURL with the command below:
You should get notified on the registered device and the notification should be logged on your Twilio Notify service console.
NOTE: Because PWAs require https to work properly, you’ll have to start up an ngrok tunnel on port 8000 to be able to access the app from a device separate from your development machine (i.e to make it accessible from somewhere other than localhost).
Conclusion
The Twilio Notify API helps us send notifications to our users on different platforms and in this article, we have seen how it can be used with PWAs. You can find the complete source code for this tutorial on Github and if you have an issue or question, please feel free to create a new issue on the repository.
Michael Okoko is a student currently pursuing a degree in Computer Science and Mathematics at Obafemi Awolowo University, Nigeria. He loves open source and is mostly interested in Linux, Golang, PHP, and fantasy novels! You can reach him via:
Related Posts
Related Resources
Twilio Docs
From APIs to SDKs to sample apps
API reference documentation, SDKs, helper libraries, quickstarts, and tutorials for your language and platform.
Resource Center
The latest ebooks, industry reports, and webinars
Learn from customer engagement experts to improve your own communication.
Ahoy
Twilio's developer community hub
Best practices, code samples, and inspiration to build communications and digital engagement experiences.