skip to content

Data Loading & Caching

Where a route reads and writes data, which copies of the result are kept and who may see each, and what a write invalidates. Interviewers probe it because stale or leaked data is the classic bug here.

on this pageshow

explore

questions

page 1 of 2

In a meta-framework, how does a cached copy kept on the server differ in scope from one kept in the browser tab?

level: juniorimportance: must knowfreq 62%

answer

  1. ask who can be served it
  2. two axes: lifetime and scope
  3. server-side is not per-user
  4. the key defines the audience
  5. browser copy dies on full load

basics

~20 s

A copy kept on the server is shared: any request matching its key can be served it, including another visitor's. A copy in the browser tab is private to that browser and gone on a full page load.

solid answer

~50 s

Cached copies differ on two axes: how long they live, and who can be handed them. Within one render, a dedupe cache holds a read only until the response is finished, so it is private to that request. A store on the server outlives the request, so a copy placed there can be handed to any later request whose key matches, including a different, signed-in visitor. The rendered output of a route held for reuse is shared the same way. The payload the browser keeps for routes it has already visited is private to that tab and disappears on a full page load. The practical rule: a read derived from who is asking belongs in the request-scoped or the browser-side copy, never in a copy that outlives the request unless every identity input is part of the key.

go deeper

for a junior

Remember the two questions to ask of any cache: how long does the copy live, and who can be served it. Server-side means shared; in the browser tab means private to that browser.

for a middle

Be able to walk the placements from shortest-lived to longest and say what discards each one, and explain why the cache key is what actually decides a shared copy's audience.

for a senior

Show that you decide placement when you write the read, and that you can point at which placement a suspect response came from using age or debug headers, a latency cliff, and a second identity.

for a principal

Frame it as a blast-radius decision: a wrong lifetime costs staleness, a wrong scope costs a data-exposure incident, and the two deserve different review and testing rigour.

## Why placement is the real question Saying a value is "cached" says almost nothing useful. Two properties decide whether a cache is a performance win or a security incident: - **lifetime** - when the copy is discarded; - **scope** - who can be served it. A meta-framework typically offers several places to put a copy, and those places differ on both axes. Choosing where a read's result lives is choosing its audience, and that choice is made at the moment you mark a read as cacheable - not later, when someone reads the output. ## The places a copy can live | Placement | Lives in | Discarded when | Who can be served it | | --- | --- | --- | --- | | Per-render dedupe | the work of one request | that response is finished | only that request | | In-process store | one server instance's memory | the process restarts, or on eviction | every visitor that instance serves | | Stored render output | storage the server reads from | expiry, removal, or a new build | every visitor whose request matches the key | | Client route payload | the browser tab's memory | a full page load, or the tab closes | that one browser | Several consequences fall straight out of that table: - Only the first and last rows are **naturally private**. The middle two are **shared by construction**: putting a copy "on the server" makes it less private, not more. - A copy that outlives the request also outlives the **identity** that produced it. Storage keeps bytes and a key; it keeps no memory of who was signed in when those bytes were made. - Lifetime and scope are independent. A browser's copy can easily outlive an in-process copy - a process restart wipes one and not the other - and it is still the more private of the two. - The **key** is what defines the audience of a shared copy. Two requests that compute the same key are, as far as the cache is concerned, the same request. ## The rule that follows A read is *personalised* when its result depends on who is asking. Signals that you are looking at one: - it is derived from a session cookie, an authorization header, or any identity token; - it varies by role, entitlement, plan, or tenant; - it contains anything you would not be willing to print on a fully public page. A personalised read is safe in a request-scoped copy (it dies with the response) and in the browser's own copy (it never leaves that browser). It is unsafe in a copy that outlives the request **unless every input that varies the output is part of the key** - and per-user keys carry their own costs in entry count and hit rate. ## What a leak actually looks like The failure mode is mechanical, not exotic: 1. A request from a signed-in visitor produces a render that includes their name, balance, or order list. 2. That render is stored under a key computed from the URL - identity was never an input. 3. A later request from anyone else resolves to that same key and is served the stored bytes, name and all. The symptom is intermittent and correlated with traffic: it only happens when a second request arrives while the copy is still around. It usually cannot be reproduced in a development server, because the placements that store output are often inactive there. And a hard refresh sometimes appears to "fix" it, which sends people hunting in the browser instead of in the store. ## Frameworks do not all ship the same set Meta-frameworks differ in how many of these placements they actually provide. Some ship all of them with caching applied by default, so the interesting work is opting particular reads out. Others ship only the client-side payload and leave everything shared to standard HTTP caching and to whatever the host puts in front of the origin. Because of that, memorising one product's tier list transfers badly. What transfers is the habit of asking, for any given read: *how long does this copy live, and who can be handed it?* ## Telling copies apart in practice When you need to know which copy answered a request, useful signals include: - an **age** or debug header on the response, where the framework or host emits one; - a **latency cliff** - a stored copy answers far faster than a render that hits a database; - changing the underlying data and seeing the response not change; - comparing a response in a fresh private window against one in the original browser, which separates a browser-side copy from a server-side one. None of that is needed to make the right decision up front. Placement is decided when the read is written, and the question to answer then is simply whether a stranger being handed this exact response would be acceptable.

  • Does signing out remove a personalised copy that was already stored on the server?
    No. Signing out changes what the browser sends on the next request; it does not reach into a store on the server. A copy produced during a signed-in render stays there until it expires or is explicitly removed, and any later request whose key matches can still be served it. That is the reason a personalised render should not be placed in a shared copy in the first place.
  • If two server instances each hold their own in-process copy, what does that change about scope?
    Scope becomes per instance: the same URL can be answered from a different copy depending on which instance takes the request, so behaviour looks inconsistent between refreshes and a problem may reproduce on one instance and not another. Anything you do to a copy in memory affects only the instance you did it on.

A copy on a shared server is like a printout left in the office printer tray: whoever walks up next can pick it up. A copy in your browser tab is the note in your own pocket.

saying these in an interview costs you the question

  • Thinks a cache on the server is private because it is server-side
  • Assumes signing out removes copies already stored on the server
  • Treats cached as one thing with one lifetime and one audience
  • Believes the browser's route payload is visible to other visitors
  • Cannot say who would be served a given stored copy
open as a page

On a server-rendered page, why is the data the server already read embedded in the page instead of fetched again in the browser?

level: juniorimportance: must knowfreq 72%

basics

~20 s

Embedding the server's already-loaded result lets the browser's data cache start full, so the first client render reads it from cache instead of issuing a second request for data the user is already looking at.

open as a page

A meta-framework serves a route from a stored copy. After the underlying data changes, what makes visitors see the new value?

level: juniorimportance: must knowfreq 70%

basics

~20 s

A stored copy never learns that the data behind it changed. It becomes unusable only when the application names that copy and marks it stale, or when its freshness window elapses and the next request produces the value again.

open as a page

In a meta-framework, what does a route module's own server-side data fetch do before the route's HTML exists?

level: juniorimportance: must knowfreq 78%

basics

~20 s

A route's server-side data fetch runs when the URL matches, gathers the data that route needs, and hands it to the render, so the first HTML already carries the content and the browser needs no follow-up request.

open as a page

In a meta-framework, what does it mean for a route to handle a write itself instead of posting to a separate API?

level: juniorimportance: must knowfreq 72%

basics

~20 s

A route-handled write is a server-side function the framework publishes at its own URL, so an ordinary HTML form can POST straight to it. The framework runs it on the server, then re-renders the route or follows a redirect.

open as a page

How can a meta-framework's cached copies be addressed for invalidation, and what does each scheme require at read time?

level: middleimportance: must knowfreq 64%

basics

~20 s

You can only invalidate what you can name. Copies are addressed by the route path that produced them, by labels attached when the value was stored, or by nothing, leaving elapsed time as the only instrument.

open as a page

When nested route segments each declare a server-side data fetch, what decides whether they run in parallel or become a waterfall?

level: middleimportance: must knowfreq 68%

basics

~20 s

Independently declared segment fetches can all start the moment the URL is matched, so they overlap. A waterfall appears when a child's fetch needs a value the parent's fetch produced, forcing it to wait for the parent to resolve first.

open as a page

Why is a server-side write function that a route exposes as a POST target treated as a public endpoint?

level: middleimportance: must knowfreq 70%

basics

~20 s

Exposing the function gives it a URL, and any client can send it any payload. Nothing the rendering page decided travels with the request, so identity, permission and input checks must live inside the handler.

open as a page

A user reports seeing another account's data on a page; how do you work out which cached copy served that response?

level: seniorimportance: must knowfreq 55%

basics

~20 s

Treat a wrong-account report as an exposure, not staleness: reproduce with two identities against a production-shaped build, then eliminate placements one at a time, using age or debug headers and a latency cliff as evidence.

open as a page

A page seeds its client cache from a server-side copy shared between users, and a visitor sees another account's data. What went wrong, and how is it prevented?

level: seniorimportance: must knowfreq 60%

basics

~20 s

A personalised value was read from a copy not keyed by identity, then frozen into a document free to be reused. Keep identity-dependent reads out of shared copies, and never let a document carrying a personal seed be shared.

open as a page

In a meta-framework's server render, how does a per-render deduplication cache differ in lifetime and scope from a store that outlives the request?

level: middleimportance: should knowfreq 52%

basics

~20 s

Per-render dedupe collapses repeated reads within one request and is thrown away with that response, so it is private to the request. A store that outlives the request keeps the copy for later requests, which makes it shared.

open as a page

Once the browser's data cache has been seeded from the server render, what decides whether the client trusts that entry or refetches it?

level: middleimportance: should knowfreq 58%

basics

~20 s

A seeded entry is an ordinary cache entry: its worth depends on when the server actually read the value and on the freshness policy applied. Trust it while that window holds; revalidate when it lapses.

open as a page

When is fetching in the browser the right choice on a server-rendered page, and when does it recreate the waterfall the route read removed?

level: middleimportance: should knowfreq 52%

basics

~20 s

Fetch in the browser for data that is personal, live, or only needed after an interaction. It becomes a waterfall when a request can only start once its component renders, so a nested component's request waits on its parent's.

open as a page

On a client-side navigation between two URLs in the same nested route chain, which segments' server fetches re-run?

level: middleimportance: should knowfreq 57%

basics

~10 s

Segments whose match changed re-run; identically matched segments above them are commonly reused. The server runs the same code again but returns a serialised data payload for the changed segments, not a whole document.

open as a page

What parts of the incoming request reach a route module's server-side data fetch, and what must it never trust about them?

level: middleimportance: should knowfreq 52%

basics

~20 s

A route's server-side fetch receives the whole request: matched path segments, query string, method, URL and headers. All of it is attacker-controlled, so validate every value before it reaches a query, a path, a redirect or an authorisation decision.

open as a page

When a route-handled write finishes, when should it return field errors as data and when should it redirect instead?

level: middleimportance: should knowfreq 62%

basics

~20 s

Return data when the user must stay in the form: field-level errors rendered back into the same route, with their input preserved. Redirect on success, so the result is a plain GET that can be reloaded and bookmarked without resubmitting.

open as a page

Five nested segments each ask for the signed-in user while rendering one page — how does that stay a single lookup?

level: middleimportance: should knowfreq 52%

basics

~20 s

A request-scoped memo: the first call performs the lookup, stores the in-flight result under the current request, and every later call in that render reuses it. The entry dies with the request, so nothing carries over to the next visitor.

open as a page

A team adds the signed-in user's id to a shared cache key on the server; what does that fix, and what does it not?

level: seniorimportance: should knowfreq 48%

basics

~20 s

Putting the user id in the key stops one user's entry reaching another, but only if every other input that varies the output is there too. It does not make the store private, and it trades hit rate for entry count.

open as a page

A scheduled import changes content outside the app. How should that change reach the app's cached copies, and what must the receiving endpoint guarantee?

level: seniorimportance: should knowfreq 46%

basics

~20 s

The app never ran that write, so it needs an ingress: an endpoint the external system calls, naming what changed, which maps that onto labels or paths and purges. Authenticate it, validate it, keep a window.

open as a page

One write changes a value that many routes read. How do you determine what to invalidate, and how do over-wide and under-wide labels fail differently?

level: seniorimportance: should knowfreq 55%

basics

~20 s

Let the readers declare what they depended on, so one write purges by data name and reaches every consumer. Over-wide labels fail loudly as wasted work and origin load; under-wide labels fail silently as content nobody notices is stale.

open as a page

A nested segment's server-side data fetch throws while the route is being served — what does the user end up seeing?

level: seniorimportance: should knowfreq 46%

basics

~20 s

The framework catches the throw and renders the nearest declared failure state in the matched chain, leaving the segments above it intact. How much of the page that replaces depends on where the nearest boundary sits.

open as a page

When the same route-handled write arrives twice, from a double click or a retry, what makes it safe to receive?

level: seniorimportance: should knowfreq 48%

basics

~20 s

Only a server-side rule makes it safe: a one-time token recorded as used, a uniqueness constraint, or an effect that assigns a value rather than a delta. Client-side guards reduce the odds, never the risk.

open as a page

After a route-handled write succeeds and the app lands on the list route, why might the list still show the old rows?

level: seniorimportance: should knowfreq 54%

basics

~20 s

Because the write changed the store but nothing told the route's read steps they were out of date, so the response reused a result computed before the change. A write has to mark what it invalidated, not just redirect.

open as a page

A signed-out visitor requests a deep personalised URL — how do you send them to sign-in and back afterwards?

level: seniorimportance: should knowfreq 55%

basics

~20 s

Capture the requested path and query, redirect to the sign-in route carrying it, and after a successful sign-in redirect there only if it is a local relative path; otherwise fall back to a default destination.

open as a page

How would you set an app-wide policy for which cached copies may hold a personalised read?

level: principalimportance: should knowfreq 38%

basics

~20 s

Default-deny: anything derived from who is asking stays request-bound or in the browser. Classify reads by audience, make placement visible at the call site, split pages so personal holes sit outside shared copies, and enforce it with a two-identity test.

open as a page

How would you set an organisation-wide policy for what gets seeded into the page versus fetched by the browser?

level: principalimportance: should knowfreq 44%

basics

~20 s

Decide it on four axes: who the data belongs to, how fast it goes stale, how big it is, and whether the first screen needs it. Publish that as a default with a byte budget and a payload review.

open as a page

When an invalidation cannot be guaranteed to reach every stored copy, how do you decide which copies to trust it for and which to bound with a short window?

level: principalimportance: should knowfreq 42%

basics

~10 s

Classify content by the cost of being stale, then per copy ask whether you can name it, whether the purge is acknowledged, and what the worst case is. Elsewhere the window is the promise.

open as a page

Should the per-request session read run before routing, in a layout, or in each route module, and how do you decide?

level: principalimportance: should knowfreq 45%

basics

~20 s

Each site buys something different: a pre-routing step is uniform but coarse and cannot hand data to the route, a layout renders shared chrome, and a per-route read is explicit and precise. Most real apps use all three deliberately.

open as a page

showing 1–30 of 32