IntermediateNetworking, storage, and workers~45 min

Networking, Storage, and Workers

HTTP methods and caching semantics, choosing among cookies/Web Storage/IndexedDB, offline strategy with service workers, and offloading CPU-bound work to a web worker.

By the end of this lesson, you can

  • Choose the right HTTP method and read a Cache-Control header's basic directives
  • Choose among cookies, Web Storage, and IndexedDB based on size, lifecycle, and server-visibility trade-offs
  • Explain what a service worker intercepts and why that enables an offline strategy
  • Offload CPU-bound work to a web worker and communicate with it via message passing

Why it matters

Asynchronous Basics and Data Fetching treated fetch as a black box that returns JSON. Real applications need to understand what’s actually happening over HTTP, choose the right place to persist data client-side, and — for CPU-heavy work or offline support — reach beyond the main thread entirely.

HTTP methods and status classes

The HTTP methods used in nearly every application, and their intended semantics
MethodPurposeIdempotent?
GETRetrieve a resource, no side effectsYes
POSTCreate a resource, or a non-idempotent actionNo
PUTReplace a resource entirelyYes
PATCHPartially update a resourceNot guaranteed
DELETERemove a resourceYes

Recall idempotent from Asynchronous JavaScript in Depth — a GET/PUT/DELETE is safe to retry automatically; POST generally isn’t. Status codes fall into five classes: 1xx informational, 2xx success, 3xx redirection, 4xx client error (the request itself was wrong), 5xx server error (something failed on the server’s side, even with a valid request).

Caching with Cache-Control

Cache-Control: max-age=3600, must-revalidate

Cache-Control tells the browser (and any intermediate cache) how long a response can be reused without asking the server again. max-age sets that duration in seconds; no-store disables caching entirely; no-cache means “always revalidate with the server before using a cached copy,” which — despite the name — doesn’t disable caching, just requires a check first.

Choosing where to store client-side data

Cookies, Web Storage, and IndexedDB — real trade-offs, not interchangeable defaults
StorageSize limitSent to server automatically?Best for
Cookies~4KB per cookieYes — with every matching requestSession/auth tokens the server needs to see
localStorage / sessionStorage~5–10MBNoSmall client-only data — preferences, a draft, a flag
IndexedDBLarge (browser-dependent, often GBs)NoStructured, queryable, offline-capable app data

A cookie being sent automatically with every matching request is a double-edged trait: convenient for authentication, but a real cost on every request whether needed or not — and a genuine security surface, which is why an auth cookie should carry HttpOnly (inaccessible to JavaScript, mitigating XSS token theft) and Secure (HTTPS-only) flags, set by the server.

Service workers and offline strategy

A service worker is a script that runs separately from any page, capable of intercepting every network request the page makes and deciding how to respond — from the network, from a cache, or a combination:

// sw.js — registered from the page with navigator.serviceWorker.register('/sw.js')
self.addEventListener('fetch', (event) => {
  event.respondWith(
    caches.match(event.request).then((cached) => {
      return cached || fetch(event.request); // cache-first strategy
    }),
  );
});

Different offline strategies fit different data: cache-first (above) suits assets that rarely change; network-first-with-cache-fallback suits data that should be fresh when possible but usable when offline; a documented, deliberate choice per resource type matters more than any single “correct” strategy.

Web workers for CPU-bound work

A web worker runs JavaScript on a genuinely separate thread — useful specifically for CPU-heavy work (image processing, complex calculations) that would otherwise block the main thread and freeze the UI. Workers communicate with the main thread by message passing, not shared memory:

JavaScriptOffloading a CPU-heavy calculation to a worker
// main.js
const worker = new Worker('calc-worker.js');
worker.postMessage({ numbers: [1, 2, 3 /* ...thousands more */] });
worker.onmessage = (event) => {
  console.log('Result:', event.data.result);
};
// calc-worker.js
self.onmessage = (event) => {
  const result = event.data.numbers.reduce((sum, n) => sum + expensiveCalculation(n), 0);
  self.postMessage({ result });
};

Data passed via postMessage is copied using the structured clone algorithm — recall structuredClone() — not shared by reference, so a worker can never directly mutate the main thread’s objects (or vice versa); everything crosses as a copy.

Check your understanding

A response has Cache-Control: no-cache. Does the browser store a copy of it?
Or reveal the answer without checking

Answer:Yes — it stores it, but must revalidate with the server before reusing it on a later request
Despite the name, no-cache doesn't disable caching — it requires revalidation with the server before the cached copy can be used. no-store is the directive that actually prevents storing a response at all.

An app needs to store thousands of structured, queryable offline records (a reading list with metadata). Which client-side storage fits best?
Or reveal the answer without checking

Answer:IndexedDB
IndexedDB is designed for large amounts of structured, queryable data with an offline-capable lifecycle. Cookies are too small and add overhead to every request; localStorage/sessionStorage are synchronous, string-only, and impractical at this scale.

Practice: warm-up

For each scenario, name the best-fit client-side storage: (1) a short-lived “dismissed this banner” flag, (2) an authentication token the server must see on every request, (3) an offline product catalog with hundreds of searchable items.

Stuck? Reveal one hint at a time.

  1. Hint 1

    A flag that only this tab/session needs, and doesn't need to survive a browser restart, has a storage option matched almost exactly to that lifecycle.

  2. Hint 2

    Something the server needs to see automatically on every request is the defining trait of one specific storage type.

  3. Hint 3

    Large, structured, queryable data points toward the storage designed for exactly that.

Reveal the answers

Try the problem yourself before reading this. There is often more than one reasonable approach — treat this as one worked example, not the only correct answer.

1. A dismissed-banner flag -> sessionStorage (or localStorage if it should persist across sessions)
2. An auth token the server must see       -> a cookie (ideally HttpOnly + Secure)
3. An offline, searchable product catalog  -> IndexedDB

Practice: apply it

Sketch (as commented pseudocode) a service worker fetch handler implementing a network-first, cache-fallback strategy: try the network first, and only fall back to a cached response if the network request fails.

Stuck? Reveal one hint at a time.

  1. Hint 1

    This is the inverse of the cache-first example in the lesson — try fetch() first, inside a try/catch or a .catch().

  2. Hint 2

    On failure, fall back to caches.match(event.request); if neither succeeds, there's genuinely no response to give.

Reveal a reference solution

Try the problem yourself before reading this. There is often more than one reasonable approach — treat this as one worked example, not the only correct answer.

self.addEventListener('fetch', (event) => {
  event.respondWith(
    fetch(event.request).catch(() => {
      return caches.match(event.request); // network failed — fall back to cache
    }),
  );
});

Summary

  • GET/PUT/DELETE are idempotent and generally safe to retry automatically; POST usually isn’t. Status codes group into five classes, 4xx meaning the request was wrong and 5xx meaning the server failed on a valid request.
  • Cache-Control: no-cache still caches — it forces revalidation before reuse; no-store is what actually disables caching.
  • Cookies are sent automatically with every matching request (a cost and a security surface); Web Storage is simple, synchronous, and client-only; IndexedDB fits large, structured, offline-capable data.
  • A service worker intercepts every network request a page makes, enabling deliberate, documented offline strategies — cache-first for stable assets, network-first-with-fallback for data that should be fresh when possible.
  • A web worker runs on a separate thread for CPU-bound work, communicating with the main thread only via message passing — data is copied via structured clone, never shared by reference.