
PWA: Web as App
PWA characteristics and implementation

PWA characteristics and implementation
Public APIs face unexpected traffic floods without proper protection. Rate limiting, API key management, and IP restrictions to protect your API.

Started with admin/user roles but requirements grew complex. When RBAC isn't enough, ABAC provides attribute-based fine-grained control.

With 3 services needing separate logins, SSO unified authentication. One login grants access to everything.

Password resets were half my support tickets. Passkeys eliminate passwords entirely, but implementation is more complex than expected.

While building my service, I had a problem. Users kept requesting, "I want to add this to my home screen." But making a native app... just thinking about it gave me a headache.
I thought, "I already built it as a web app, do I really need to make a native app too?" Then I discovered PWA (Progressive Web App). They said it's technology that makes web work like an app.
I was skeptical at first. "How can web become like an app?" But when I actually built one... I was amazed.
The first thing I tackled with PWA was offline support. Regular websites just show the "No internet connection" dinosaur game when offline. But PWAs are supposed to work offline.
I wondered how this was possible, and the key was Service Worker. Simply put, it's a JavaScript file that acts as a bridge between the browser and server. It intercepts network requests, checks cached data first, and only requests from the server if not found.
When I first registered a Service Worker, I did it like this:
// main.js - Register Service Worker
if ('serviceWorker' in navigator) {
window.addEventListener('load', () => {
navigator.serviceWorker.register('/sw.js')
.then(registration => {
console.log('Service Worker registered:', registration);
})
.catch(error => {
console.log('Service Worker registration failed:', error);
});
});
}
And the actual Service Worker file looked like this:
// sw.js - Service Worker implementation
const CACHE_NAME = 'my-pwa-v1';
const urlsToCache = [
'/',
'/index.html',
'/styles.css',
'/app.js',
'/logo.png'
];
// Install phase: Save necessary files to cache
self.addEventListener('install', (event) => {
event.waitUntil(
caches.open(CACHE_NAME)
.then((cache) => {
console.log('Cache opened');
return cache.addAll(urlsToCache);
})
);
});
// Intercept network requests
self.addEventListener('fetch', (event) => {
event.respondWith(
caches.match(event.request)
.then((response) => {
// Return cached data if available
if (response) {
return response;
}
// Otherwise fetch from network
return fetch(event.request);
})
);
});
After building this, my website worked even when I disconnected the internet! I was genuinely amazed when I first saw it. "Wow, this actually works?"
After implementing offline support with Service Worker, the next step was add to home screen. When users tap "Add to Home Screen" in the browser menu, an icon appears just like a real app.
For this, I needed a Web App Manifest file. One JSON file defines the app's name, icon, colors, etc.:
{
"name": "My Service",
"short_name": "Service",
"description": "My service built as PWA",
"start_url": "/",
"display": "standalone",
"background_color": "#ffffff",
"theme_color": "#2196F3",
"icons": [
{
"src": "/icons/icon-192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/icons/icon-512x512.png",
"sizes": "512x512",
"type": "image/png"
}
]
}
Then link this manifest file in HTML:
<link rel="manifest" href="/manifest.json">
<meta name="theme-color" content="#2196F3">
After this, when I opened my site on mobile, an "Add to Home Screen" popup appeared. When added, an icon appeared just like a real app, and clicking it opened in fullscreen without the browser address bar.
"display": "standalone" was the key. This hides the browser UI and makes it look like an app.
The most surprising thing about PWA was push notifications. Sending push notifications from a website! This was also thanks to Service Worker.
First, request notification permission from the user:
// Request notification permission
async function requestNotificationPermission() {
const permission = await Notification.requestPermission();
if (permission === 'granted') {
console.log('Notification permission granted');
// Register push subscription
const registration = await navigator.serviceWorker.ready;
const subscription = await registration.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey: 'YOUR_PUBLIC_KEY'
});
// Send subscription to server
await sendSubscriptionToServer(subscription);
}
}
Then handle push messages in Service Worker and display notifications:
// sw.js - Handle push notifications
self.addEventListener('push', (event) => {
const data = event.data.json();
const options = {
body: data.body,
icon: '/icons/icon-192x192.png',
badge: '/icons/badge-72x72.png',
vibrate: [200, 100, 200],
data: {
url: data.url
}
};
event.waitUntil(
self.registration.showNotification(data.title, options)
);
});
// Handle notification click
self.addEventListener('notificationclick', (event) => {
event.notification.close();
event.waitUntil(
clients.openWindow(event.notification.data.url)
);
});
With this, I could send notifications even when users weren't viewing my site. Just like a native app!
An important concept I learned while using Service Worker was caching strategies. Caching everything is good for offline but prevents updates, while caching nothing makes it unusable offline.
The strategy I used:
1. Cache First: Static files like images, CSS, JS
self.addEventListener('fetch', (event) => {
if (event.request.url.match(/\.(jpg|png|css|js)$/)) {
event.respondWith(
caches.match(event.request)
.then(response => response || fetch(event.request))
);
}
});
2. Network First: Dynamic content like API data
self.addEventListener('fetch', (event) => {
if (event.request.url.includes('/api/')) {
event.respondWith(
fetch(event.request)
.then(response => {
// Save network response to cache
const responseClone = response.clone();
caches.open(CACHE_NAME).then(cache => {
cache.put(event.request, responseClone);
});
return response;
})
.catch(() => {
// Use cache on network failure
return caches.match(event.request);
})
);
}
});
This way, static files load quickly, API data stays fresh, and users can still see the last data offline.
After building a PWA and applying it to my service, the pros and cons were clear.
PWA is technology that makes web work like an app using Service Worker and Web App Manifest. It enables app features like offline support, add to home screen, and push notifications on the web, and deployment without app stores makes development and updates easy. While not as powerful as native apps, it's sufficient for most web services and, most importantly, much easier to build.