In a nested route chain where a parent and its child both carry guards, what order runs and why does it matter?
answer
- the matched chain, outermost first
- first non-proceed verdict wins
- inner guards depend on outer results
- a moved route loses its ancestor's check
- guard the redirect target too
basics
~20 sRouters normally run a matched chain's guards from the outermost route inward, stopping at the first redirect or cancel. A parent can establish the session and a child narrow by role — but only for chains that include that parent.
solid answer
~50 sMatching a URL produces a chain of routes from the outermost layout down to the leaf, and guards run along that chain outermost first, sequentially, short-circuiting on the first verdict that is not "proceed". The ordering is what makes composition work: an outer guard resolves the session, an inner one narrows by membership, and the leaf's guard checks the specific role. It also means a child guard's assumption is only as sound as the tree shape: move a route out from under the guarded parent, or reach it through a chain that omits a pathless group, and the inner check runs with nothing before it. Order guards from most general to most specific, guard the redirect target too so two guards cannot bounce to each other, and do not rely on an outer guard to notice a permission revoked mid-session.
go deeper
Remember that guards belong to routes, and a nested URL matches several routes at once, so more than one guard can apply to a single navigation.
Explain outside-in ordering and short-circuiting, and why an inner guard can rely on what an outer one resolved. Know that the first non-proceed verdict ends the navigation.
Diagnose the real failures: a route moved out of a guarded subtree, a pathless group missing from some chains, and a redirect cycle between two complementary guards.
Decide whether protection comes from tree shape or from explicit composition, and write that rule down — it determines whether a routine refactor can silently unprotect a screen.
## The matched chain A nested route table describes containment: a workspace route contains a project route, which contains a settings route. Matching a URL walks that table and produces a **matched chain** — an ordered list of route entries from the outermost match to the leaf — and the rendered screen is that chain composed through outlets. Guards belong to entries in the chain, so "which guards run" is answered entirely by "which entries matched". ## Outermost first, and short-circuiting Routers run the chain's guards **from the outside in**, one after another, and stop at the first verdict that is not "proceed": 1. The outermost guard runs. If it redirects or cancels, the navigation ends there and **no inner guard runs at all**. 2. Otherwise the next guard inward runs, and so on to the leaf. 3. Only when every guard in the chain has proceeded does the navigation resolve data, commit and render. Sequential outside-in ordering is a design choice with reasons behind it: - **Dependency.** An inner guard usually needs what an outer one established — an identity, a workspace, a loaded membership. Running them together would force each to resolve those again. - **Least work.** The cheapest, most general check fails fastest, so an anonymous visitor never triggers the role lookup an inner guard would perform. - **Deterministic destination.** If two guards would redirect to different places, sequential ordering makes the outer one win every time. Run them concurrently and the destination depends on which resolved first. Concurrency is still legitimate for **independent** checks — a feature-flag check and a role check that share no inputs — but the moment one reads what another produces, ordering is the contract. ## Who checks what | Level | Typical responsibility | Bad fit at this level | |---|---|---| | Outermost app or shell route | is there a resolved session at all | anything about one record | | Mid-level area route | membership of the workspace, tenant or organisation the path names | the leaf's specific role rules | | Leaf route | the role or capability this one screen needs | resolving the session from scratch | ## What a child guard may assume Only what **every chain that can reach it** guarantees. That is a statement about the route table, not about intent, and it is where ordering bugs come from: - A route is **moved** during a refactor and is no longer nested under the guarded parent; its own guard still passes, and the screen is now reachable without a session check. - The session check sits on a **pathless grouping route** that some chains do not include, so the same leaf is protected via one path and unprotected via another. - A **catch-all or not-found entry** sits outside the guarded subtree and renders something that assumes an identity. - A leaf is reachable both as an **index route** of the guarded parent and directly by a longer path added later. Two defences: put the general check on the route every protected route genuinely nests under, or stop relying on tree shape and **compose** the check into each protected route's guard list explicitly. A test that navigates directly to the deep URL — cold, with no session — is what keeps either honest. ## Composition and loops Guards compose well because their verdicts compose: a list of checks that each either proceed or produce a destination is easy to reason about. The failure mode to plan for is a **cycle**. Sign-in guarded by "only for visitors without a session" and a protected route guarded by "only with a session" will bounce to each other if either reads an unresolved session, or if a redirect target is itself inside the guarded subtree. Rules that avoid it: never redirect while the answer is unknown, keep redirect destinations outside the subtree whose guard sent the user there, and make one terminal route — a default screen — that no guard redirects away from. ## Guards are per navigation, not per change Guards are part of the navigation pipeline, so they run when a navigation happens. They are **not** reactive: nothing re-runs them because state changed. Routers also differ in how much of the chain they re-run on a navigation within the same parent — some re-run every guard in the matched chain, others only the guards of routes being newly activated. The consequence is the same either way: a role revoked mid-session may not be noticed by any guard while the user stays inside that subtree. The server rejecting the next request is what catches it, and the app should treat such a rejection as a signal to refresh its permission data and re-evaluate the current route. ## What interviewers listen for - That you say outside-in and short-circuit, and can justify sequential ordering. - That you treat a child guard's assumption as a property of the route table, and can name how a refactor breaks it. - That loops between two guards come up as a real hazard with a concrete rule to prevent them. - That you do not claim guards notice a mid-session permission change.
- Why run a chain's guards sequentially rather than all at once?Because inner guards usually consume what an outer one resolved, short-circuiting avoids work for visitors who fail the cheapest check, and concurrent guards that would redirect to different destinations make the landing place depend on which resolved first. Run checks concurrently only when they share no inputs.
- How do you keep a child guard's assumption about its ancestors honest through refactors?Either attach the general check to the single route every protected route truly nests under, or compose it into each protected route's guard list so tree shape stops being load-bearing. Then add a test that navigates cold and directly to the deep URL, which is the only navigation that exercises the full chain from nothing.
- What creates a redirect loop between two guards, and how do you stop it?A redirect target that is itself guarded by the complementary rule — sign-in requiring no session, the protected route requiring one — combined with a guard that decides while the session is still unknown. Prevent it by never deciding on an unresolved session, keeping destinations outside the subtree that sent the user away, and having one terminal default route.
saying these in an interview costs you the question
- Assumes the most specific guard runs first
- Relies on tree shape after routes are moved around the table
- Redirects into a subtree whose own guard sends the user back
- Thinks guards re-run whenever state changes
- Expects an outer guard to catch a permission revoked mid-session
- Runs interdependent guards concurrently and hopes the order holds