Let's say you have the route /A/B/C (B being a chi...

Created on: April 11, 2025

Answered using O1 by Chat01

Question

Let's say you have the route /A/B/C (B being a child of A, C being a child of B), with each node having a guard and a resolver... what would be the execution order of guards + resolvers overall? What changes if B and C would be lazy loaded modules or components?

Thought

Piecing together route guards

I’m noting the sequential execution of canActivate and canDeactivate guards, followed by resolvers, for routes. The exact order depends on the router's parsing approach.

Understanding the sequence

I'm noting that the router typically loads the main route first, then child routes sequentially, checking canActivate, running resolvers, and finally canDeactivate when leaving a route.

Tracing sequence steps

OK, let me see. I’m outlining the sequence of canActivate and resolvers for both non-lazy and lazy-loaded routes. Lazy routes only trigger when the user navigates to them.

Ensuring accurate configurations

OK, let's finalize the execution sequence: non-lazy routes follow canActivate guards first, then resolvers. For lazy routes, canLoad triggers before canActivate, adjusting guard actions.

Clarifying route resolution

I’m refining the sequence of route resolution, distinguishing between lazy and non-lazy routes. Lazy routes involve an extra canLoad step before canActivate and resolvers.

Clarifying the sequence

I’m highlighting the logical sequence: canActivate for A, B, and C, followed by resolvers for C, B, and A. This progression ensures clear understanding.

Understanding the sequence

I’m pulling together that route resolvers and guards in Angular are processed top-down. Parent route resolvers activate first, followed by the child route's activation.

Balancing execution

I'm curious about the order of execution: parent route resolvers and guards come first, then the child's canActivate, and finally the child's resolvers, with canActivateChild potentially altering this sequence.

Adding lazy route steps

OK, let me see. For lazy-loaded routes like B and C, an additional canLoad guard checks before activation, following the typical sequence of parent and child's guards and resolvers.

Reasoned about route execution order for 30 seconds

Answer

In Angular, both the parent and child routes run their guards and resolvers in a predictable top‐down order: first the parent’s guards and resolvers, then the child’s, then the grandchild’s, and so on. Lazy loading adds an extra “canLoad” step (if you use it) and defers loading of the child route configuration until actually needed. Below is the typical sequence in more detail.


1. Non‐Lazy Example: /A/B/C

Suppose A, B, and C are all part of the same (eagerly loaded) module, each with its own canActivate guard and resolve:

  1. A’s canActivate guard runs.
  2. If A is allowed, A’s resolvers run.
  3. B’s canActivate guard runs.
  4. If B is allowed, B’s resolvers run.
  5. C’s canActivate guard runs.
  6. If C is allowed, C’s resolvers run.
  7. Finally, the router completes navigation to /A/B/C.

Note: If there are canDeactivate guards, they generally run when you leave a route rather than when you enter. Also, if any guard fails, the router cancels navigation immediately and won’t attempt further steps.


2. Lazy‐Loaded Example

If instead B is a lazily loaded module (and within it, C is also a child route):

  1. A’s canActivate guard runs.
  2. If A is allowed, A’s resolvers run.
  3. The router sees that /B is a lazy‐loaded module. If you have a canLoad guard on the lazy‐loaded module, that canLoad guard runs first to decide whether the module can even be fetched.
  4. If canLoad passes (or there is none), Angular loads B’s module.
  5. B’s canActivate guard runs.
  6. If B is allowed, B’s resolvers run.
  7. Inside B’s module, if /C is a child route, then C’s canActivate guard runs.
  8. If C is allowed, C’s resolvers run.
  9. Router completes navigation to /A/B/C.

Essentially, once you lazy load a module, the router has to fetch its entire routing configuration at runtime (assuming canLoad permits it). Then, Angular proceeds with the usual guard→resolver sequence for routes inside that newly loaded module.


What if C is also lazy‐loaded?

If C is another lazy-loaded route within B’s module, you would see a similar pattern:

  1. Activate and resolve A.
  2. canLoad + canActivate/resolve for B.
  3. Then inside B’s module, you encounter another lazy load for C:
    • canLoad (if present on C’s module),
    • then C’s canActivate,
    • then C’s resolvers.

Key Takeaways

  1. Top‐Down: Parent route guards and resolvers always run before the child’s.
  2. Resolvers After Guards: For a given route, its resolvers only run after all of its canActivate guards pass.
  3. Lazy Loading:
    • Potential extra canLoad step that decides if the child module can even be fetched.
    • The child’s guards/resolvers run only after the module is actually loaded.

That’s really the main difference: when a route is lazy loaded, the module’s routing configuration (and thus its guards/resolvers) only come into play after Angular has fetched that module—assuming no canLoad guard prevents it. Otherwise, the order of execution (guard → resolver for parent, then guard → resolver for child) remains the same.

Question

Are you sure that the order is not guard A, guard B, guard C, resolver A, resolver B, resolver C?

Thought

Angular Router's operation involves running guards based on the route configuration order, then resolving data through resolvers in their respective sequences.

Ensuring a hierarchical approach, each route segment's canActivate guards are executed first, followed by its resolvers, then moving onto the next child route's guards and resolvers, and so on.

Confirming the order of execution: guards and resolvers are applied to each route segment in sequence, beginning with guard A, followed by resolver A, then guard B, and resolver B, continuing similarly for subsequent segments.

Ensuring accuracy

I’ve verified that the correct order for route execution is guard A, then resolver A, followed by guard B, then resolver B, and so forth, adhering to the route hierarchy.

Aligning the process

It's interesting to see that the validation of guards and resolvers is assessed on a per-route basis, ensuring that execution aligns with the documented engaging route process.

Reasoned for 12 seconds

Answer

In Angular’s router, the guards and resolvers do not all run in one “guards first, resolvers second” sweep. Instead, they run per route segment in a top‐down fashion. Concretely, for a route tree like /A/B/C:

  1. A’s canActivate guards run.
  2. If A is allowed, A’s resolvers run.
  3. B’s canActivate guards run.
  4. If B is allowed, B’s resolvers run.
  5. C’s canActivate guards run.
  6. If C is allowed, C’s resolvers run.

Only once a route’s guards have passed does the router run that route’s resolvers. Then it proceeds to the child route.

Hence, the actual order is:

A.guard → A.resolver → B.guard → B.resolver → C.guard → C.resolver

rather than:

A.guard → B.guard → C.guard → A.resolver → B.resolver → C.resolver

Why it Works This Way

  • The router doesn’t even attempt resolvers on a route until it’s sure the route can be activated (i.e., the guards pass).
  • Once a route is activated and its resolvers finish, the router moves on to the child route.

Lazy Loading Consideration
If B or C are lazy‐loaded, there may be an additional canLoad guard step before the router even fetches their route configuration. Once the module is loaded, the pattern above (guards → resolvers per route level) stays the same.

Question

Ok, but if A/B/C is accessed, why would we first resolve the data for A and B if the G guard denies access?

Thought

Understanding resolver triggers

OK, let me see—resolvers in Angular might be running even with denied access by a guard, causing data confusion. This suggests a need for clear rules on resolver activation.

Confirming resolver triggers

Angular processes route segments top-down. If route B's guard denies access, its resolvers don't activate. This aligns with the logical routing sequence.

Piecing together Angular's routing

Angular's top-down routing means resolvers for routes A and B only run if their guards allow access. Otherwise, the router bypasses them, ensuring no data fetching.

Evaluating Angular's logic

I'm focusing on Angular's hierarchical routing: all guards are evaluated first. If any deny access, further processing stops, ensuring efficient and targeted resolver activation only when necessary.

Clarifying route activation

Angular's top-down route activation ensures guards and resolvers are evaluated sequentially. If a guard at any point denies access, subsequent routes and their resolvers are not activated.

Understanding route activation

OK, let me see. Angular's top-down approach ensures that B's resolver runs when activated, unless a subsequent route's guard denies access. This is part of the thorough routing process.

Navigating Angular's routing

Angular activates routes top-down, checking each segment and resolver. This method ensures comprehensive access and data availability for each route.

Navigating route outcomes

Angular ensures each route is fully activated before moving to the next. If any route's guard fails, its resolver isn't triggered, effectively managing access and dependencies.

Reasoned about guards and resolvers order for 41 seconds

Answer

First, it’s important to clarify that Angular processes each segment of the route tree in order. It does not run all the guards first and then all the resolvers. Instead, it activates each route (i.e., runs its guards and, if they pass, runs its resolvers) before moving to the next child.

So for a route /A/B/C, Angular does:

  1. A’s guards (e.g. canActivate).

    • If they fail, navigation stops immediately.
    • If they pass, then A’s resolvers run.
  2. Move on to B’s guards.

    • If they fail, navigation stops immediately.
    • If they pass, B’s resolvers run.
  3. Finally, C’s guards.

    • If they fail, navigation stops.
    • If they pass, C’s resolvers run.

But Why Resolve A and B if C’s Guard Will Deny Access?

You might wonder: “If we know the route eventually fails at C, why bother resolving data for A and B first?”

Short answer:

  • Because Angular doesn’t “peek ahead” and pre-check all child guards before activating the parent route.
  • Each route segment is activated in turn: the parent must be activated before the child can even be attempted.

Concretely:

  1. When you enter /A/B/C, Angular first checks and activates A.
  2. Having activated A, it then checks and activates B.
  3. Only then does it even try to activate C, at which point C’s guard might fail and cancel the navigation.

At that point, the router has already activated and resolved data for A and B. There is no built-in mechanism that defers parent resolvers until all child guards have been confirmed.

If a parent’s data isn’t actually needed unless you get all the way down to a child route—that’s more of a design question around how the routes and resolvers are organized. The router’s default approach remains “activate the parent first (guard + resolver) before the child even comes into the picture.”


Could We Make It Check C’s Guard First?

Out of the box, Angular doesn’t have a “look-ahead” mode that tries to evaluate child guards before running parent resolvers. The sequence is top-down. However, you have a few alternatives:

  • Use a “composite” guard higher up (e.g., on B) that also checks conditions for the child (C) so you fail early without loading B’s data.
  • Move the logic needed to deny at C into canMatch/canLoad if it’s a separate lazy module—then it won’t load the entire module or resolvers if the user can’t proceed anyway.
  • Structure your resolvers so that only the route that truly needs the data resolves it (i.e., move resolvers to the child route itself if that data is only needed at the child).

In other words, if it is wasteful to resolve A or B’s data when you know 90% of the time the user might fail at C, you can restructure or unify your guard logic to fail as early as possible and skip unneeded resolvers.


Summary

  • Angular’s default flow is: Guard → Resolver on each route level, starting from the top parent to its child, then grandchild, etc.
  • If a guard denies access at a lower level, everything above it has already been activated and resolved—there is no “rollback” or “look-ahead.”
  • If you need to skip expensive parent resolvers unless the entire chain will pass, you must design your guards and resolvers accordingly (e.g., earlier guards, canLoad/canMatch, or resolvers on the child instead of the parent).

Share this Q&A