Blog
Mert Özen's blog posts on software development, backend, frontend, and DevOps.
Fetch in Depth: Streaming Responses, AbortSignal.any, keepalive, fetchLater and Angular 22's Fetch Backend
To process a large response without waiting for all of it, read response.body line by line with TextDecoderStream; AbortSignal.any combines a timeout with the user's cancel, and keepalive and fetchLater send data while the page closes. With real output from Chromium 158 and WebKit 27.2 we show the HTTP/2 requirement of upload streaming, keepalive's 64 KiB limit, WebKit differences and route-level withXhr() for upload progress in Angular 22's HttpClient.
Which Browser Storage? localStorage, IndexedDB, Cache API, OPFS, cookieStore and Quota Management
Put a small preference in localStorage, queryable records in IndexedDB, HTTP responses in the Cache API, files in OPFS and values the server also reads in cookieStore; all share the origin's quota and can be evicted without persist(). In Chromium 158 and WebKit 27.2 we show localStorage's 5 MiB limit, the Cache API ignoring max-age, OPFS failing in an ephemeral session, estimate()/persist() results and Safari's 7-day deletion rule.
Using IndexedDB in Angular: Schema Versioning, Transactions and an Offline Outbox
In IndexedDB the schema changes only inside onupgradeneeded when the version goes up, and a transaction commits by itself at an await that is not IndexedDB; so the network work comes first and the writes after. We show per-version migrations, TransactionInactiveError and another tab blocking an upgrade in Chromium and WebKit, then build the reading list with idb in Angular and an outbox that sends offline reviews without duplicates using idempotency keys.
The Service Worker Lifecycle and Caching Strategies: install, activate, waiting, skipWaiting and Stale-While-Revalidate
A new service worker waits in waiting until every tab of the old version closes or skipWaiting is called; reloading the only tab is not enough. With a framework-free worker we walk through the lifecycle in Chromium and WebKit, and look at skipWaiting mixing an old page with a new chunk, updateViaCache, cache-first/network-first/stale-while-revalidate strategies, the HTTP cache trap and a kill switch that withdraws the worker.
An Offline-Capable App with the Angular Service Worker: ngsw-config, dataGroups, SwUpdate and Safe Deploys
The Angular Service Worker caches app files with assetGroups and API responses with dataGroups, and detects versions from the hashed ngsw.json the build produces; you announce a new version with SwUpdate.versionUpdates and let the user do a full reload. On a production build we show the offline scenario, VERSION_READY, the unrecoverable state after old files are deleted, deploy cache headers and withdrawing the worker with safety-worker.js in Chromium and WebKit.
Making a PWA Installable: Web App Manifest, Install Flows, navigator.install and App Badging
What makes a PWA installable is its manifest: write the id once and keep it, separate any and maskable icons, show the install offer in Chromium with beforeinstallprompt only after the user has seen some value, and give Safari users Add to Home Screen instructions. With real output from Chromium 158 and WebKit we show the identity calculation, the installability check, the errors of navigator.install() and App Badging for unread orders.
Web Push Notifications: VAPID, Angular SwPush and Sending Notifications from ASP.NET Core
In Web Push the browser gives a subscription bound to the backend's VAPID public key; when an order ships, the backend sends a VAPID-signed, aes128gcm-encrypted message to that endpoint and the Angular Service Worker shows the notification. We run the flow end to end with Angular SwPush, ASP.NET Core 10 and Lib.Net.Http.WebPush through a local test push service, and show permission UX, an endpoint check against SSRF, 404/410 cleanup and what happens when the key changes.
Using Web Workers in Angular: ng generate web-worker, Comlink and Relieving the Main Thread
Putting a heavy calculation into an async method does not take it off the main thread; in Angular you move the work into a worker generated with ng generate web-worker and wrap it in a service with Comlink, or split it with scheduler.yield(). For a 5,000-book client-side search with the CPU 4 times slower we measure the slowest interaction at 408 ms on the main thread, 56 ms with yield and 40 ms in the worker, and cover the tsconfig trap, cancellation and what to move to a worker.
Tabs and the Page Lifecycle: BroadcastChannel, Web Locks, visibilitychange and the Navigation API
In an app running in several tabs, you give shared work to a single tab with Web Locks, spread the result and events such as sign-out to the other tabs with BroadcastChannel, and use pagehide and visibilitychange instead of unload on close. In Bookstore we show the outbox sending two POSTs from two tabs instead of four, a leader tab doing the polling alone, unload no longer running in Chromium 158, and the Navigation API with Angular's experimental support.
What Changed in Browser APIs and the Angular Service Worker from 2020 to 2026: Reading Baseline
Baseline tells whether a feature is in every core browser; Angular 22 targets the browsers that were widely available on a fixed date (Chrome 119, Firefox 119, Safari 17), so newly available APIs such as the Navigation API still need feature detection. A report that reads the status of the series' features from web-features, the milestones from 2020 to 2026, the API changes in the Angular Service Worker and an audit checklist for an old Angular PWA.
Browser APIs That Talk to the Device: Async Clipboard, Web Share, Screen Wake Lock, Fullscreen, Permissions and Geolocation
You call the clipboard, share, fullscreen and location APIs from inside a click, read the permission state up front with the Permissions API and give each a feature-detected fallback; apart from the wake lock, they are rejected without a user activation. In Bookstore we put them in one DeviceCapabilities service and show, in Chromium 158 and WebKit 27.2, how the activation gets used up, why ClipboardItem should get a promise, and a nearest-warehouse lookup that keeps the location in the browser.