Home >Web Front-end >JS Tutorial >Progressive Web Apps: New FE systems
In enterprise environments, we often take stable internet connectivity as something given. However, real world conditions frequently challenge this assumption, potentially disrupting critical business operations. This article details how we transformed a traditional online only ERP system into a more reliable system with resilient offline capable solution. By leveraging browser-based storage solutions like IndexedDB, employing synchronization mechanisms, and using Progressive Web Apps (PWA).
Initially, the system followed a traditional client server architecture where all business logic resided on the backend. While this architecture works well in environments with reliable connectivity, it presented several challenges:
So defining this, we had to improvise and see how we can make things better and also do without connecrivity since it isnt available initially, we implemented an offline system with some internet required using Progressive Web Apps (PWA), effectively moving critical business logic to the frontend while maintaining data integrity and synchronization with the core ERP system.
IndexedDB: For offline data storage and caching, we used IndexedDB via the Dexie.js library to provide a robust, client-side database that supports structured data storage. below is a simple example of how to set up a database with Dexie
// Initialize IndexedDB using Dexie.js import Dexie from 'dexie'; interface LocalUsers{ id:string; name:string; role:string; dob:string; phone_no:string } interface LocalTrx { id: string; syncId:string; created_at:string; amount:string; isSynced:boolean; modified:string; } export class ArticleDatabase extends Dexie { transaction!: Table<LocalTrx>; users!: Table<LocalUsers>; constructor(){ super("articleDB") } this.version(1).stores({ // define the fields you'll like to query by or find items by within the indexDB transactions: 'id, date, amount, isSynced', users: 'id, name, role' }); } //create the db export const db=new ArticleDatabase() // Open the database db.open().then(() => { console.log("Database opened successfully!"); }) .catch((error) => { console.error("Failed to open database:", error); }); // Adding a new transaction to IndexedDB import db from ../db async function addTransaction(transaction) { try { const trx = await db.transactions.add(transaction) console.log("Trx added", trx) } catch (err) { console.log("Failed creating trx", err) } }
Service Workers: These act as a proxy between the app and the network, enabling offline functionality by caching resources and intercepting requests to ensure that critical data remains accessible during disconnection.
//service workesr can be setup easily, recently by default nextJS apps do come with service workes with vite, you can use the vite-pwa plugin
Background Sync: This allows us to sync data once the network is available again, ensuring that transactions are not lost and updates are made automatically once connectivity is restored.
System Architecture Flow
The architecture was divided into three main phases: initialization, transaction processing, and synchronization. The flowchart below shows how data flows between these stages.
When the system starts, it checks the network connection:
If the device is online, it fetches the latest master data from the server and updates the local IndexedDB.
If the device is offline, it loads data from IndexedDB, ensuring that users can continue working without disruption.
When users perform a new transaction:
Local data is validated and stored in IndexedDB.
An optimistic UI update is used to immediately show the result to the user, providing a smooth and responsive experience.
When connectivity is restored:
Data is synchronized in batches with the server, either by manually clicking the sync button or after some certain timeframe.
If the sync fails (e.g., due to a slow connection), the transaction is added to a list of failed transactions and retried later.
Since we manage everything on the frontend how reliant is our service against securing customers Information.
Authentication & Authorization
In any enterprise system, securing sensitive user information is critical. Our solution ensures that:
JWT-based Authentication is used for secure user sessions.
Role-based access control ensures that only authorized users can perform specific actions.
Secure token storage is handled using browser-based mechanisms such as localStorage for added security.
To mitigate the risks of using locally stored tokens, we:
Trigger safe removal of user tokens upon logout.
Ensure that sensitive data is deleted from IndexedDB when the session ends or when the user logout from the system. Note: if transactions are left unsynced we display that to the logged in user and enforce them to sync before they logout.
Data Integrity & Conflict Resolution
Syncing data between the client and server introduces potential issues with data integrity, especially if multiple devices or users are making changes to the same data offline. To address this:
We validate all transaction details (e.g., quantities, amounts) before syncing to ensure that there are no discrepancies.
We assign unique IDs to each transaction to prevent duplication during synchronization.
Conflict resolution strategies are employed to handle situations where data changes are made on multiple devices while offline. For example, we use a timestamp approach.
//we try to make sure the offline is considered first, because it's the important bit of the system.
// Initialize IndexedDB using Dexie.js import Dexie from 'dexie'; interface LocalUsers{ id:string; name:string; role:string; dob:string; phone_no:string } interface LocalTrx { id: string; syncId:string; created_at:string; amount:string; isSynced:boolean; modified:string; } export class ArticleDatabase extends Dexie { transaction!: Table<LocalTrx>; users!: Table<LocalUsers>; constructor(){ super("articleDB") } this.version(1).stores({ // define the fields you'll like to query by or find items by within the indexDB transactions: 'id, date, amount, isSynced', users: 'id, name, role' }); } //create the db export const db=new ArticleDatabase() // Open the database db.open().then(() => { console.log("Database opened successfully!"); }) .catch((error) => { console.error("Failed to open database:", error); }); // Adding a new transaction to IndexedDB import db from ../db async function addTransaction(transaction) { try { const trx = await db.transactions.add(transaction) console.log("Trx added", trx) } catch (err) { console.log("Failed creating trx", err) } }
Network Security
Given that data is transmitted over the network once connectivity is restored, we ensured:
Rate limiting to prevent abuse and ensure that too many requests do not overwhelm the server with a 429 response hence why we originally worked with batch updates.
Encryption of data during transit using SSL/TLS.
Token expiration and secure token management, ensuring that stale or expired tokens are automatically removed from the client-side storage.
While IndexedDB is a solid choice for client-side data storage in PWAs, there are other options available depending on the application's complexity and requirements:
SQLite via WebAssembly (WASM): Some developers opt to use SQLite via WASM for more advanced data management, particularly when dealing with larger datasets or complex queries. However, integrating SQLite via WASM introduces additional complexity, such as performance concerns and browser compatibility (e.g How sqlite made Notion faster).
Web Storage API (localStorage/sessionStorage): For simpler applications that don’t require complex querying or large datasets, the Web Storage API may be a viable alternative. It is easier to implement but has limitations in terms of storage capacity and querying capabilities.
As web technologies continue to evolve, so do the possibilities for applications like this. Emerging trends include:
I myself can’t wait to explore how these technologies will transform the landscape of offline and distributed applications. With the rapid advancements in powerful machines and laptops, we have the opportunity to harness this increased computing power to deliver even more sophisticated and efficient software for users. At the same time, we must not forget the importance of catering to mobile devices and less capable devices, ensuring that our solutions are accessible and optimized across all platforms. The potential is enormous, and I’m excited to continue pushing the boundaries of what's possible with PWAs.
We’ll be taking things up. Using Djuix.io as our backend and React / Angular for our frontend, we would implement a proper basic flow. Stay tuned for more updates as we continue to enhance our approach to building amazing apps.
Anyway, I hope you enjoyed this and learned something new. I certainly did. I'd also love to hear your thoughts and experiences.
Until then.
The above is the detailed content of Progressive Web Apps: New FE systems. For more information, please follow other related articles on the PHP Chinese website!