The point of progressive web apps is to make optimal use of the ever-evolving capabilities of browsers. One such advanced browser capability is to store data in a browser for offline app functionality. Browsers now use several NoSQL and NewSQL technologies for this purpose.
Apache Ignite, and Google Spanner DB are popular NewSQL DBs. MySQL Cluster is an optimized and highly scalable new SQL interface. Offline DB management is the focus of our ultimate tutorial today, and we’ll look at several code examples to illustrate the best methods.
While the focus of our ultimate progressive web application development tutorial is NoSQL for offline core content data storage, we need to lay some groundwork.
Before we delve deeper into NoSQL DB features, such as NoSQL document storage, let’s explore the tangibles of PWAs to create a context and motivation for offline DB management. As mentioned, the PWAs represent an ideal to leverage every available new technology in web design.
Service Workers and Web Manifests provide much of the functionality toward reaching these ideals. Developers are now empowered to build PWAs with features including:
Mobile apps and web apps compete for supremacy, but each has a limiting set of inherent advantages and disadvantages.
Mobile apps offer higher performance and richer user experience, but the costs are reduced real-time interactivity and pressure on the limited device storage.
Web apps are highly interactive in real-time, but also highly dependent on Internet connectivity and speed. Service Workers arise as a solution to resolve the disadvantages, especially web apps. Service workers improve web app performance with the following enhancements:
Mechanically, service workers sit between server and device to manage requests and supply content. The service worker functions as a network proxy in browsers to manage HTTP requests.
Ultimately service workers provide the mechanism for efficient cache use which makes offline functionality possible.
The web manifest includes metadata for a web app and is a valuable JSON container that exposes linking and content to search. Manifests make it possible to deposit a web app’s icon on a device home screen alongside installed apps!
From the user’s point of view, the web app operates as a native mobile app. And the offline functionality, we will soon discover, is a crucial part of this user experience. Important metadata stored in a manifest includes:
As you can imagine, the manifest essentially converts a bookmark into a native app on a device’s home screen. This looks to the user just like an installed app.
If the web app then performs accordingly, there is no essential difference. Coding for offline work goes a long way toward achieving the necessary performance!
Central to creating an app-like feel in progressive web apps is push technology. Push notifications are subscribe-publish interaction in which the server initiates a transaction.
The capability of the app host server to initiate action which is then executed on a device is much more interactive.
The real-time interactivity achieved with push notifications gives the web app more autonomy than with apps that rely solely on one-sided pull requests. Push requests update an app’s page display automatically.
As with the overall concept of PWAs, progressive enhancement is a guiding architectural principle, rather than a specific technology. But building pages according to this principle is definitely facilitated by specific technologies.
The primary goal here is to ensure that the core functionality and core website content of a page are accessible to all browsers.
Ensuring core functionality on all browsers is not a trivial pursuit. It effectively requires a survey of technologies supported by all browsers before the first line of code can be written!
Compliance, in other words, is often more difficult to guarantee in the scope of new technologies. Other targets of progressive enhancement include:
When core page content works for all users across all browsers, a web app has a standard metric. End users report similar and consistent results.
To wrap up our survey of PWAs and motivate our NewSQL exploration, we just need to mention the benefits of security and linking. These are characteristic advantages of web apps over native apps rather than coding methods.
Because web apps run transactions through HTTPS, the encryption makes them more secure than native apps.
Finally, linking an icon on the home screen to the URL of a web app will save a lot of storage space over installed apps for mobile devices, especially as the number of apps grows.
Now that we have defined the objectives of PWAs, it’s time to bite off one of the major chunks and really dig into it. We’re talking about the offline database for progressive web app development.
Given the progressive enhancement goal, you may find it ironic that ServiceWorker is not supported by all browsers yet. It likely will achieve global support, so we will press onward.
To achieve offline performance in a PWA, we need to learn how to integrate service workers into our applications.
To add a ServiceWorker your site must use a secure HTTPS connection. We will implement a simple website with static HTML, a CSS file, and a JavaScript file with a few images, and add offline support.
First, we will register the ServiceWorker. We pass a JavaScript resource to .register which will execute in the ServiceWorker context. Registration returns a promise, which makes it possible to track successful or failed registration. Have a look at this example of ServiceWorker registration:
if('serviceWorker' in navigator) { console.log('thisCLIENT: Registration in progress..'); navigator.serviceWorker.register('/service-worker.js').then(function() { console.log('thisCLIENT: Registration complete.'); }, function() { console.log('thisCLIENT: Registration failed.'); }); } else { console.log('thisCLIENT: ServiceWorker not supported.'); }
The ServiceWorker script generally originates from the host’s domain root folder. If the user’s browser supports ServiceWorker, it will send a request for /service-worker.js and try to install it. ServiceWorkers are event-driven which means that your app code should be stateless. When a ServiceWorker is inactive it is shut down. Do not code any memory state dependencies. To install the ServiceWorker include this script:
self.addEventListener("thisInstallation", function(event) { console.log('WORKER: installation event in progress.'); event.waitUntil( /* Add caches as built-in promise API */ caches .open(version + 'fundamentals') .then(function(cache) { return cache.addAll([ '/', '/css/globals.css', '/js/globals.js' ]); }) .then(function() { console.log('WORKER: installation complete'); }) ); });
Cached content is now available to be served in offline mode. The ServiceWorker caches all designated content for offline use.
For our illustration of creating a Spanner DB for offline database management, we will use PHP. Most developers are familiar with sending SQL queries through PHP scripts.
So, this will have a familiar feel. Spanner also supports API calls for most popular languages. Here, we’ll step through the use of the Cloud Spanner client library.
The first step is to create a Cloud Spanner instance along with a database. We will illustrate how to write to the DB, run SQL queries on the data.
The code sample below will demo on how to update the database schema, as well as how to update data with read-write transactions. This function example shows the syntax which will easily translate from SQL:
function create_myDatabase($instanceId, $databaseId) { $spanner1 = new SpannerClient(); $instance1 = $spanner->instance($instanceId); if (!$instance1->exists()) { throw new \LogicException("Instance $instanceId not exist"); } $operation1 = $instance1->createDatabase($databaseId, ['statements' => [ "CREATE TABLE tbl_Artists ( ArtistId INT32 NOT NULL, FirstName STRING(25), LastName STRING(25), ArtistsInfo BYTES(MAX) ) PRIMARY KEY (ArtistId)", "CREATE TABLE Albums ( ArtistId INT32 NOT NULL, AlbumId INT32 NOT NULL, myAlbumTitle STRING(MAX) ) PRIMARY KEY (ArtistId, AlbumId), INTERLEAVE IN PARENT tbl_Artists ON DELETE CASCADE" ]]);
Spanner supports adding a secondary key index to a table. This makes possible using the index to run SQL queries on your data. Here is an example of how to run a query in Spanner:
$database = $instance1->database($databaseId); $results = $database->execute('SELECT "Hello World" as test'); foreach ($results as $row) { print($row['test'] . PHP_EOL); }
And inserting data to a table is easy to do with this sample function:
function insert_myData($instanceId, $databaseId) { $spanner = new SpannerClient(); $instance1 = $spanner->instance1($instanceId); $database = $instance1->database($databaseId); $operation = $database->transaction(['singleUse' => true]) ->insertBatch('Artists', [ ['ArtistId' => 1, 'FirstName' => 'MarK', 'LastName' => 'Herbertson'] … }
Altering database schema is easy with Spanner. To add a new column to a table requires a schema update.
Cloud Spanner supports updating a database schema even while the database is online serving requests and queries! Schema updates do not require the database to be offline, and do not even need to lock tables!
This is an extraordinary feature for PWA performance because it dramatically reduces app maintenance downtime. Undoubtedly, this is one of the premium features which enterprises will pay for: Cloud Spanner is not free, a departure from the Google norm.
The purpose of Cache API is to store and retrieve network requests and responses. The work of Cache API falls into two main categories.
The Cache API system can store ordinary requests and responses which occur during the execution of a web app.
Cache API can also store data for the sole purpose of maintaining the data in the cache for offline use.
In particular, Cache API enables a ServiceWorker to cache network requests in order to respond even when the web app is offline. But crucially the cache can also be used as general storage for offline data and content.
A Firefox cache, for example, can use 10% of available space! Following is a basic example to set up with Cache API. The first step creates a Request object using a URL for the object to be stored:
const request = new Request('/images/picture1.jpg');
const myImageBlob = new Blob([data], {type: 'image/jpeg'});
const myImageResponse = new Response(myImageBlob);
const ThisStringResponse = new Response('Hello PWA World!!!);
Above, the Response object constructor will accept a variety of data types. Common types used include ArrayBuffers, strings, Blobs, FormData, and more. Google’s Cache API NoSQL documentation contains a complete reference on Response objects. The core knowledge is available for PWA progressive web app development.
An Indexed database (IDB) is a type of NoSQL database that makes it easy for a web app to display content even when running in offline mode. IDB is perhaps the most popular and standard way to store data through a browser.
The web browser standard interface does transactions on the local database. IDB uses a collection of indexed JSON objects for this client-side database management. And this is contrasted with the typical MySQL database, where the data store resides on the server.
IndexedDB is a low-level type language API for coding client-side database management. IDB is ideal for storing large datasets – perfect for Big Data – and large structured data sets. IDB’s API uses indexes to power high-performance searches.
IndexedDB is specifically a transactional database system. The interface resembles SQL. But IndexedDB is JavaScript-based. And it is object-oriented, unlike SQL (however, see our PostgreSQL tutorial for an object-oriented version of SQL).
While SQL uses tables with fixed-columns, IndexedDB stores object indexed with a key instead. The structured clone algorithm (SCA) dictates the type of content which can be indexed.
In the next code example, we will illustrate how to open a DB, specify the database schema, and open a connection to the IndexedDB. Then we will simply retrieve and display data saved persistently in the browser:
var request = window.indexedDB.open("ThisTestDatabase", 3); var db1; var request = indexedDB.open("ThisTestDatabase"); request.onerror = function(event) { alert("IndexedDB not supported on this browser"); }; request.onsuccess = function(event) { db1 = event.target.result; }; var objectStore1 = transaction.objectStore("customers"); customerData.forEach(function(customerdb) { var request = objectStore.add(customerdb); request.onsuccess = function(event) { // event.target.result === customerdb.ssn; };
NewSQL and NoSQL technologies do not comply with rigorous ACID standards. This means that PWA development reveals a challenge to other idealized methods that enterprise developers follow as guiding lights. ACID is one such beacon to be challenged by SQL DB platforms. So, let’s review ACID and see where PWA development does not comply.
Reading this strictly, it is clear that even the best NoSQL database platforms cannot enforce compliance with these ideals. NoSQL database use cases individually show variation, as enterprises develop coding standards to emulate compliance in various degrees.
In performance races between native mobile apps and web apps which load in browsers native apps previously dominated the cyberscape. They loaded faster and were optimized for mobile. But nowadays, thanks to AJAX and its spawn such as AngularJS, dynamic and progressive apps are in the race again. In this tutorial, we learned how to build a PWA and leverage a NewSQL database.
We have seen how guiding principles and extended functionality define the architecture of progressive web development.
The objective is to exploit every technological innovation in browsers today. The nuances and advantages leveraged by PWAs include service workers, push notifications, progressive enhancement design ideology, and much more.
We hope you will continue your study of PWA apps by building your own examples. And don’t forget to tune in next week for the next Bytescout ultimate tutorial.