skip to content

In Angular's ngsw-config.json, how do the dataGroups strategies performance and freshness answer an API request, and what do maxAge and timeout control?

level: middleimportance: should knowfreq 30%

answer

  1. data is not versioned
  2. cache first until maxAge
  3. network first, cache on timeout
  4. 0u timeout trick
  5. only GET and HEAD are cached

basics

~10 s

performance (the default) answers from cache while the entry is younger than maxAge; freshness goes to the network and falls back to the cache only when the request fails or exceeds timeout.

solid answer

~50 s

`dataGroups` cache responses such as API calls, which are not versioned with the app. Each group matches `urls` and has a `cacheConfig`: `maxSize` (entry count), `maxAge` (how long an entry is valid), an optional `timeout`, an optional `refreshAhead` and a `strategy`. With `performance`, the default, a cached entry younger than `maxAge` is returned with no network request; once an entry is at least `refreshAhead` old, serving it also starts a background refresh. With `freshness`, the worker fetches from the network first and falls back to a cached entry still within `maxAge` only if the request errors or exceeds `timeout`. `freshness` with `timeout: '0u'` emulates stale-while-revalidate. Only `GET` and `HEAD` are cached; a `POST` or `PUT` to a matching URL evicts the cached entry and goes to the network. Groups are matched in order, first match wins.

go deeper

for a junior

Recall that performance serves from cache first and freshness goes to the network first with the cache as a fallback.

for a middle

Explain maxAge, timeout, maxSize and refreshAhead, the 0u trick, group ordering, and that only GET and HEAD are cached.

for a senior

Pick strategies per endpoint from staleness tolerance and offline needs, and plan for mutating requests failing offline.

for a principal

Set a caching policy for API data per sensitivity and freshness class, including what must never be cached on shared devices.

## Data groups versus asset groups `ngsw-config.json` has two kinds of cache configuration. **Asset groups** cover files that belong to an application version and change with each build. **Data groups** cover everything else the app requests, typically API responses, which are **not versioned** with the app. A data group is cached according to a policy you write: ```json { "dataGroups": [ { "name": "inspections-api", "urls": ["/api/inspections/**"], "version": 1, "cacheConfig": { "strategy": "freshness", "maxSize": 200, "maxAge": "7d", "timeout": "3s" } } ] } ``` ## The cacheConfig fields | Field | Meaning | |---|---| | `maxSize` | Maximum number of cached responses; an open-ended cache can exceed storage quota and be evicted | | `maxAge` | How long a response stays valid, as a duration string such as `3d12h` (units `d`, `h`, `m`, `s`, `u`) | | `timeout` | How long to wait for the network before using the cache, in the same format | | `refreshAhead` | Age at which serving a cached entry also starts a background refresh (see the note below) | | `strategy` | `performance` (default) or `freshness` | `version` (default `1`) is a separate field on the group. Raising it discards entries cached under older versions, which is how you handle a backwards-incompatible API change. ## performance: cache first For a `GET` or `HEAD` request: 1. If a cached response exists and has not passed `maxAge`, **return it without a network request**. 2. If `refreshAhead` is set and the entry is old enough, also refresh it in the background. 3. Otherwise go to the network and cache the response. If `timeout` is set and the network is slower, the worker returns a `504 Gateway Timeout` and caches the late response when it arrives. Suited to data that changes rarely: reference lists, avatars, lookup tables. It trades staleness, bounded by `maxAge`, for speed. ## freshness: network first 1. **Fetch from the network**, racing it against `timeout` if one is set. 2. If the network answers in time, cache and return the response. 3. If the request **errors or times out**, return the cached response **if it is still within `maxAge`**, while the network response is still cached when it eventually arrives. Entries older than `maxAge` are evicted when looked up, in both strategies. 4. If nothing is cached, wait for the network after all. Suited to data that changes often and must be current when possible, with the cache as an offline fallback. Setting `timeout` to `0u` returns cached data (within `maxAge`) almost immediately while refreshing in the background, which emulates **stale-while-revalidate**. ## Rules that surprise people - **Only non-mutating requests are cached.** `GET` and `HEAD` go through the strategy; a mutating request such as `POST`, `PUT` or `DELETE` to a matching URL **removes** that URL's cached entry and is sent to the network. Offline, it fails; the service worker does not queue it. - **Order matters.** Groups are checked in the order written, and the first matching group handles the request, so specific patterns go before broad ones. - **Opaque responses** (cross-origin without CORS) are cached by default only in `freshness` groups; `cacheOpaqueResponses` overrides that. - **Responses are shared cache entries.** Caching authenticated or per-user responses needs a deliberate decision about who else could read them on that device. ## Worked timeline A `performance` group with `maxAge: "1d"` and `refreshAhead: "20h"` caches `/api/checklists` at 08:00: 1. Requests until 04:00 the next day are answered from cache with no network call. 2. From 04:00 the entry is at least 20 hours old, so a request is still answered from cache and the worker also refreshes the entry in the background; a successful refresh resets its age. 3. If no refresh succeeded, from 08:00 the entry has expired and the next request goes to the network. A note on `refreshAhead`: the configuration guide describes it as a time *before expiration*, but the worker in v22.2 compares it with the entry's **age** (`age >= refreshAhead`). To start refreshing a given lead time before expiry, set it to `maxAge` minus that lead time. A `freshness` group with `timeout: "3s"` for `/api/inspections` behaves differently: every request tries the network, a response within three seconds wins and is cached, and a slower or failed request gets the cached copy if it is younger than `maxAge`, while the late network response still updates the cache. The configuration guide says the `0u` trick falls back to the cache "ignoring the cache age", but in v22.2 the worker's cache lookup evicts and skips any entry older than `maxAge` for both strategies. For offline use, `maxAge` must therefore cover the longest time a device may stay offline. ## Choosing - Reference data that changes weekly: `performance`, long `maxAge`, `refreshAhead`. - Assigned work items that must be current but readable offline: `freshness` with a short `timeout` and a `maxAge` covering the longest offline period. - Per-request data that must never be stale: do not put it in a data group at all. The question interviewers are probing is whether you know which side wins: `performance` trusts the cache until `maxAge`; `freshness` trusts the network until `timeout` or failure.

  • A performance data group has maxAge 1d. The API changes an item an hour after it was cached. When does the app see the change?
    Not until the cached entry passes `maxAge`, a day after it was cached, or is refreshed early by `refreshAhead`, or is evicted by a mutating request to the same URL. Within `maxAge` the worker returns the cached response without contacting the network.
  • Why does maxAge still matter for a freshness group used offline?
    When the network errors or exceeds `timeout`, `freshness` falls back to the cache, but the worker only returns entries younger than `maxAge` and evicts older ones. With `maxAge: 1d`, data cached before a two-day offline trip is gone on day two, so set it longer than the longest offline period.

saying these in an interview costs you the question

  • freshness never uses the cache while online
  • performance checks the network on every request and caches the result
  • The Angular service worker queues POST requests made offline
  • Data groups are versioned with each app build like assets
  • The last matching data group wins