📄 Page
1
Progressive Web Apps with React Implementing A Practical Handbook Web Apps A Practical Guide to create Web Apps that provides a native experience to the users Enrique Pablo Molinari using React Progressive
📄 Page
2
2 While the author has used good faith efforts to ensure that the information and instructions contained in this work are accurate, the author disclaims all responsibility for errors or omissions, including without limitation responsibility for damages resulting from the use of or reliance on this work. Use of the information and instructions contained in this work is at your own risk. If any code samples or other technology this work contains or describes is subject to open source licenses or the intellectual property rights of others, it is your responsibility to ensure that your use thereof complies with such licenses and/or rights.
📄 Page
3
Contents About the Author 4 What is this book about? 5 Development Environment 6 1 Introduction 7 1.1 Progressive? . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 1.2 And... Progressive Web Apps? . . . . . . . . . . . . . . . . . . 8 1.3 Capabilities of Progressive Web Apps . . . . . . . . . . . . . . 8 2 Crafting your First PWA 10 2.1 Configure the Development Environment . . . . . . . . . . . . 10 2.1.1 Using a Reverse Proxy . . . . . . . . . . . . . . . . . . 10 2.1.2 Using LocalTunnel . . . . . . . . . . . . . . . . . . . . 14 2.2 Task List React Application . . . . . . . . . . . . . . . . . . . 16 2.3 The Service Worker . . . . . . . . . . . . . . . . . . . . . . . . 21 2.4 Making the Web App Installable . . . . . . . . . . . . . . . . 35 2.5 Supporting Offline . . . . . . . . . . . . . . . . . . . . . . . . 43 3 Handling New Releases 57 3.1 The Update Process . . . . . . . . . . . . . . . . . . . . . . . 58 3.2 Manual Update . . . . . . . . . . . . . . . . . . . . . . . . . . 67 4 Incorporating Background Sync 69 4.1 The Background Sync Capability . . . . . . . . . . . . . . . . 69 4.2 Using IndexedDB . . . . . . . . . . . . . . . . . . . . . . . . . 70 4.3 Adding IndexedDB and Background Sync to Task List . . . . 77 4.4 Handling Syncing Errors . . . . . . . . . . . . . . . . . . . . . 94 3
📄 Page
4
About the Author My name is Enrique Pablo Molinari. I have been working in the software industry for the last 22 years, working in different software projects from different companies as developer, technical lead and architect. I’m a passionate developer and also a passionate educator. In addition to my work on the software industry, I’m teaching Object Oriented Design and Advance Database Systems at Universidad Nacional de Río Negro. Implementing Progressive Web Apps is my third book. I have also written Understanding React, a book to start learning React. And Coding an Architecture Style, a book about hands-on software architecture. You can find more about my thoughts on software development at my blog: Copy/Paste is for Word. I would be very happy if you want to ping me by email at enrique.molinari@gmail.com to send thoughts, comments or questions about this book, the others or my blog. 4
📄 Page
5
What is this book about? In this book we are going to implement a Progressive Web App (PWA) using a step-by-step approach. This learning experience starts with an already crafted React application called Task List. To create this application I have used the create-react-app tool with the cra-template-pwa template, which gives us a good starting point for building a progressive web app. We will go into the details about how to make the web app installable and after that how to improve the user experience by adding offline support to the web app. To make the learning process smooth I will first add offline support for the read-only use cases of the application. After that, I will show how to provide full offline support by using the IndexedDB database and the Background Sync. I will also explain how to update progressive web apps. How users get notified that there is a new release of the application waiting to be installed and how that actually happens. This book requires prior knowledge of JavaScript and React. 5
📄 Page
6
Development Environment There are many development environments out there, and you can choose the one you are more comfortable with. In any case if you don’t have a preference, I recommend Visual Studio Code (VS Code). And to be more productive I suggest installing the extension VS Code ES7 React/Redux/React-Native/JS snippets which provides JavaScript and React snippets. I would also suggest installing Prettier, which is a JavaScript/React code formatter. To install an extension, in Visual Studio Code, go to the File menu, then Preferences and then Extensions. You will see a search box that will allow you to find the extensions that you want to install. Finally, I really recommend configuring VS Code to format your source files on save. You can do that by going to the File menu, then Preferences and then Settings. On the search box type Editor: Format On Save. This will format your code right after you save it. 6
📄 Page
7
Chapter 1 Introduction 1.1 Progressive? No one has doubts about what a Web App mean. But progressive? What does it mean? The word progressive comes from the design philosophy known as progressive enhancement coined by Steven Champeon and Nick Finck in 2003. The idea is that you have to design your Web App to work in old devices, old browsers, with bad connectivity or no connectivity at all, to reach as many users as possible. But, it provides the best user experience for newer devices and browsers. In other words, as described by MDN (Mozilla Development Network): "The word progressive in progressive enhancement means creating a design that achieves a simpler-but-still-usable experience for users of older browsers and devices with limited capabilities, while at the same time being a design that progresses the user experience up to a more- compelling, fully-featured experience for users of newer browsers and devices with richer capabilities". As we will see later in this book, there are many new features implemented by modern browsers. For instance, there is one particularly nice that allows your Web App to work offline. However, this and many other capabilities have been added during recent years to browsers at different times and some of them partially supported. So, feature detection is the technique that we usually use to determine if that functionality can be used or not. In this way, you can build a Web App that progresses. 7
📄 Page
8
CHAPTER 1. INTRODUCTION 8 1.2 And... Progressive Web Apps? Progressive Web Apps are Web Apps that thanks to modern browser features, give users an experience similar to native mobile apps. As described by Google web.dev, on the web platform you can create and run applications to reach anyone, anywhere and on any device with a single codebase. On the other hand, platform specific applications (like mobile native apps) are very rich and reliable. They work regardless of network connection. They can interact with the camera, read your contacts, share your calendar and many others. Progressive Web Apps fit in the middle of these. They can deliver enhanced capabilities like working offline, be reliable, installable and receive push notifications, like platform specific apps. The good thing is that they run on a browser. This is possible thanks to a technology implemented by modern browsers called Service Worker. The Service Worker specification was developed by engineers from Google, Mozilla and Samsung and released in 2014. As you can see Apple, the other big player, was not involved. However, Safari does support it. We will describe this technology in the next chapter. 1.3 Capabilities of Progressive Web Apps Thanks to the service worker and other features that browsers have implemented, we now have several capabilities that allow us to provide a native experience to end users. In this section, we will briefly describe the main progressive web apps capabilities. Some of these might not be (or partially) supported in some browsers, so please make sure you check that as browsers might include them in future releases. Installable Progressive web apps can be installed in desktops or in mobile devices. Installing a PWA in mobile devices gives the users a similar experience as they have with native apps. An icon launcher is created the same as a native app, it will be listed in the applications section of the mobile operating system to be removed just like any other native app. It will run on its own windows without any browser evidence. Splash Screen Related to the previous capability, an installed PWA when launched a splash screen is shown while loading. Same experience as native apps. Offline Support Most of the PWA features were created to enable developers to create web apps that run even when connectivity has gone. The
📄 Page
9
CHAPTER 1. INTRODUCTION 9 Service Worker is the piece of software that enables us to provide offline capabilities. Background Sync This capability allows us to defer the access to server- side APIs once connectivity is back. I mean, under connectivity issues the web app might continue working, storing data on a browser’s database like indexedDB and syncing that data once connectivity is back. Push Notifications This capability enables web apps to receive push notifications in a similar way that is possible for native apps. Other Capabilities There are other capabilities like Web Share API that allows us to share content with other apps. The Contact Picker API allows us to interact with our contact list to share some limited details. The Media Session API allows us to interact with media keys on keyboards, headsets, remote controls, etc. And additionally, it is also possible to have our PWA on the Android app store, just like a native app. And the same might occur on the Apple Store, depending on the type of the application you are building. PWABuilder can help with this.
📄 Page
10
Chapter 2 Crafting your First PWA In this chapter we will convert with a step-by-step approach an existing React application, called Task List, into a progressive web app. It is a full stack application with back-end services written in Java. 2.1 Configure the Development Environment In this section I will show how to configure and set up the Task List application in your local development machine and explain its architecture. In the next section we will explore what functionality provides and what React components were created to build the front-end. The Task List application was built using the microservice architecture style. We have two back-end services: the User Authentication and the Task List. To set up these services, you will need Java 11 (or greater) and maven. Please, install them if you don’t have them. There are two ways to set up the system in your local PC: Using a reverse proxy or using localtunnel. 2.1.1 Using a Reverse Proxy To set up the development environment we first need to start the back-end services. Open a console window, go to the directory you would like to clone the source code to, then run the following commands: $ git clone https://github.com/enriquemolinari/userauth.git $ cd userauth 10
📄 Page
11
CHAPTER 2. CRAFTING YOUR FIRST PWA 11 $ mvn install $ mvn exec:java -Dsecret=bfhAp4qdm92bD0FIOZLanC66KgCS8cYVxq/KlSVdjhI=↪→ These commands will clone the UserAuth service source code, then install all the necessary dependencies, compile the source code and finally the mvn exec:java will start the UserAuth service on port 1234. The -Dsecret argument is the secure key used to encrypt the access token. Once the service finish to start, you will see on the console what is next: ... INFO io.javalin.Javalin - Listening on http://localhost:1234/ ... INFO io.javalin.Javalin - Javalin started in 247ms \o/ Now, to set up theTaskList back-end service, run the following commands: $ git clone https://github.com/enriquemolinari/tasklist.git $ cd tasklist $ mvn install $ mvn exec:java -Dsecret=bfhAp4qdm92bD0FIOZLanC66KgCS8cYVxq/KlSVdjhI=↪→ Again, once finished, you will see the following entries in the console window. This service runs on port 1235. ... INFO io.javalin.Javalin - Listening on http://localhost:1235/ ... INFO io.javalin.Javalin - Javalin started in 247ms \o/ With these in place, you have the back-end services running. Both services use the Derby embedded (in memory) database. Every time you start the service, it generates the database schema again and populate it with some data. The User Authentication database is populated with two users: guser/guser123 and juser/juser123. Each of them will have some tasks already created in the Task List database. Let’s now proceed with the React application, by cloning the starter project repository, install and serve it, with the following commands: $ git clone https://github.com/enriquemolinari/react-starter-tasklist↪→ $ cd react-starter-tasklist $ npm install $ npm start
📄 Page
12
CHAPTER 2. CRAFTING YOUR FIRST PWA 12 After that you will have the Task List React application running on port 3000. However, you won’t be able to connect with the back-end services due to the same-origin policy. The bank-end services don’t have CORS enabled by default, and even enabling it, since authentication works using cookies, the browsers won’t send them if you are on http (to use SameSite: None; you also have to use Secure). There are a couple of options to solve this. And yes, this is a bit more complicated than I would like it to be, but it is a real situation you will face if you want to build a single-page application with a microservice architecture that uses a cookie-based authentication mechanism. I will explain two options to make this work. One is using a reverse proxy and the other is using localtunnel. You will be able to start and work with the system using any of these options. Use the one you are more comfortable with. To go with the first option, you will have to install a reverse proxy. With a reverse proxy in place, the browser will only talk to it guaranteeing the same-origin policy. This architecture is depicted in figure 2.2. You can install any reserve proxy, there are many out there. I will provide configuration files for both Kong and Nginx. If you decide to install Kong, after installing it, I recommend configuring it to use the declarative config file (instead of using a database). To do that, first generate the config file with the following command: $ sudo kong config init Then, you must tell Kong, by using the kong.conf configuration file, that you want to use a declarative configuration file. Open the file kong.conf and set the database option to off and the declarative_config option to the path of your kong.yml file as in the example below: database = off declarative_config = {PATH_TO_KONG.YML} And then, open your kong.yml file, and paste there the following content: services: - name: backend-auth url: http://localhost:1234 routes: - name: backend-auth-route paths:
📄 Page
13
CHAPTER 2. CRAFTING YOUR FIRST PWA 13 - /auth - name: backend-tasks url: http://localhost:1235 routes: - name: backend-tasks-route paths: - /app - name: frontend url: http://localhost:3000 routes: - name: frontend-route paths: - / This will tell Kong to forward requests from http://localhost:8000/auth to the User Authentication back-end service running on localhost:1234. From http://localhost:8000/app to the Task List back-end service running on localhost:1235. And from http://localhost:8000/ to the React application running on localhost:3000. Finally don’t forget to start the Kong service: $ sudo kong start Now, if you navigate to http://localhost:8000/, you will see the Task List front-end. If instead of using Kong, you decide to use Nginx (if you are on Windows OS this is a good option, due to Kong at the time of writing this book does not support Windows), please go ahead and install it. After that, find the nginx config file (it will depend on your OS) and paste the following configuration: server { listen 8000; listen [::]:8000; location / { proxy_pass http://localhost:3000; } location /auth/ {
📄 Page
14
CHAPTER 2. CRAFTING YOUR FIRST PWA 14 proxy_pass http://localhost:1234/; } location /app/ { proxy_pass http://localhost:1235/; } } Note that we are forwarding to the same services as we did with Kong. Pretty similar to that. Make sure nginx is running and then if you navigate to http://localhost:8000/, you will see the Task List front-end. 2.1.2 Using LocalTunnel If you don’t want to go with a reverse proxy, the alternative I will propose is to use localtunnel. Localtunnel allows you to expose your local services with a public https URL. This option permits to share your apps while you are working and it will also allow us to test the PWA application using a mobile device. To install it, run the following command: $ npm install -g localtunnel Then, using VS Code (or your favorite editor) open the .env file from the Task List React application, cloned before in the react-starter-tasklist directory. In this file we store the URL of the back-end services that the React application consumes. By default, they will have the URL pointing to the reverse proxy, like below: REACT_APP_URI_AUTH=http://localhost:8000/auth REACT_APP_URI_TASK=http://localhost:8000/app But, if you are going to use Localtunnel, you have to change the content of the file, as shown below: REACT_APP_URI_AUTH=https://auth1.loca.lt REACT_APP_URI_TASK=https://task1.loca.lt This change requires to re-start the Node server. Note that instead of pointing to localhost it will point to real secure URLs. Now, you need to start a tunnel for each service: $ lt --port 3000 --subdomain web-epm
📄 Page
15
CHAPTER 2. CRAFTING YOUR FIRST PWA 15 $ lt --port 1234 --subdomain auth1 $ lt --port 1235 --subdomain task1 Each command above creates a public secure URL, starting with the specified subdomain and each request will forward to the localhost on the specified port. After setting this up, open a browser and navigate to each URL: https://web-epm.loca.lt, https://auth1.loca.lt and https:// c task1.loca.lt. You will have to read a friendly reminder and click a button to proceed, as illustrated by figure 2.1. Figure 2.1: Localtunnel Friendly Reminder And finally, you have to start the back-end services UserAuth and TaskList with an additional JVM parameter. Use the following command: $ mvn exec:java -Dsecret=bfhAp4qdm92bD0FIOZLanC66KgCS8cYVxq/KlSVdjhI= -Dtest-with-lt=true ↪→ ↪→ By using the -Dtest-with-lt=true parameter, we are enabling CORS for the origin https://web-epm.loca.lt and change cookie parameters to HttpOnly; domain=loca.lt; Secure; SameSite=None in order to make it work.
📄 Page
16
CHAPTER 2. CRAFTING YOUR FIRST PWA 16 Then, if you navigate to https://web-epm.loca.lt, you will see the Task List front-end. This setup will be used later in the book to test our progressive web app from a mobile device. Figure 2.2: Task List - Microservice Architecture 2.2 Task List React Application The main screenshots of the Task List application are illustrated in figures 2.3 and 2.4. In order to access their task lists a user must type first their username and password (in the login screen 2.3). That will generate a request to the UserAuth back-end service which validates the user’s credentials, and if successful it will return an access token. The access token allows the user
📄 Page
17
CHAPTER 2. CRAFTING YOUR FIRST PWA 17 to access their tasks consuming the Task List back-end services. The token is stored in an httpOnly cookie. Once authenticated, the user can retrieve their tasks. They are presented in a list as shown in figure 2.4 with their expiration date. The expiration date will have different background colors depending how close to the deadline they are. To mark a task as done (or in progress), the end user can click on the checkbox. The second task on figure 2.4 shows how a task looks like when it is marked as completed. You can also delete tasks or add new tasks. Figure 2.3: Login Page - Task List Now, if you open the source code of the application, you will see, starting with an uppercase letter, the React components created to build the app. Let’s explore each component’s responsibility. They are arranged by the following hierarchy: Listing 2.1: Task List Components Hierarchy
📄 Page
18
CHAPTER 2. CRAFTING YOUR FIRST PWA 18 Figure 2.4: juser’s Task List App.js Menu.js Welcome.js Login.js TaskList.js AddTask.js The Welcome.js component is responsible for painting the welcome message illustrated in figure 2.5. The Menu.js component paints the top header menu, see figure 2.6 (red square). The TaskList.js component shown in figure 2.6 (green square) is in charge of painting the list of tasks. It also allows the user to create, delete and mark as done task items. The AddTask.js component is responsible for painting the modal window with the form to allow the user to create new tasks (see figure 2.7, yellow square). And finally, the Login.js component paints the login form shown on figure 2.3. Looking at the source code again, in the src/server folder, you will see two JavaScript files: tasks.js and users.js. tasks.js encapsulates all the fetch requests to the Task List back-end services. While users.js, encapsulates all the fetch requests to the User Authentication back-end services. Each of the React components described above that requires access
📄 Page
19
CHAPTER 2. CRAFTING YOUR FIRST PWA 19 to a back-end service, consumes the functions exposed by tasks.js and users.js. Figure 2.5: Welcome.js component Having the development environment running and an understanding of the React components that render the Task List application, let’s start describing what is needed to transform the application into a progressive web app.
📄 Page
20
CHAPTER 2. CRAFTING YOUR FIRST PWA 20 Figure 2.6: TaskList.js Figure 2.7: AddTask.js