Skip to content

πŸ”„ Dynamic Management ​

Add, remove, and reassign outposts at runtime without restarting the application.

⚑ Static vs Dynamic ​

Static setup β€” all outposts defined at initialization:

typescript
const citadel = createNavigationCitadel(router, {
  outposts: [
    { name: 'auth', handler: authHandler },
    { name: 'analytics', handler: analyticsHandler, hooks: [NavigationHooks.AFTER_EACH] },
  ],
});

Dynamic management β€” outposts added, removed, or reassigned at runtime:

typescript
// Later, when admin module loads
citadel.deployOutpost({
  scope: NavigationOutpostScopes.ROUTE,
  name: 'admin-only',
  handler: adminHandler,
});

// When feature is disabled
citadel.abandonOutpost(NavigationOutpostScopes.ROUTE, 'admin-only');

Both approaches can be combined β€” start with core outposts, add module-specific ones dynamically.

πŸ“¦ Deploy & Abandon ​

Adding Outposts ​

typescript
// Module loaded lazily β€” registers its guards
export function registerBillingModule(citadel: NavigationCitadelAPI) {
  citadel.deployOutpost([
    {
      scope: NavigationOutpostScopes.ROUTE,
      name: 'billing:premium',
      handler: premiumHandler,
    },
    {
      scope: NavigationOutpostScopes.ROUTE,
      name: 'billing:trial-expired',
      handler: trialHandler,
    },
  ]);
}

Removing Outposts ​

typescript
// Feature flag disabled β€” remove the guard
citadel.abandonOutpost(NavigationOutpostScopes.GLOBAL, 'feature-flags');

// Module unloaded
citadel.abandonOutpost(NavigationOutpostScopes.ROUTE, ['billing:premium', 'billing:trial-expired']);

abandonOutpost returns true if the outpost was found and removed, false otherwise.

πŸ—ΊοΈ Assign & Revoke Routes ​

Outpost-to-route binding can also be managed dynamically, without changing route definitions.

Static assignment (in route config):

typescript
const routes = [
  {
    path: '/admin',
    name: 'admin',
    meta: { outposts: ['admin-only'] },
  },
];

Dynamic assignment (at runtime):

typescript
// Add outpost requirement to a route
citadel.assignOutpostToRoute('admin', 'audit-log');

// Route now requires both: ['admin-only', 'audit-log']

Dynamic revocation:

typescript
// Remove outpost requirement from a route
citadel.revokeOutpostFromRoute('admin', 'audit-log');

// Route now requires only: ['admin-only']

Duplicates are automatically filtered β€” calling assignOutpostToRoute multiple times with the same name is safe.

πŸ’‘ Use Cases ​

ScenarioDeploy / AbandonAssign / Revoke
Lazy feature modulesModule registers outposts on loadModule assigns outposts to its routes
Role-based accessDeploy role-specific outposts after loginβ€”
Multi-tenantDeploy tenant-specific guards on tenant switchAssign tenant guards to routes
A/B testingDeploy variant guard, abandon when test endsβ€”
Maintenance modeDeploy maintenance outpost globallyβ€”
Feature flagsDeploy/abandon based on flag changesAssign/revoke based on flag state

TIP

For full method signatures, see API Methods. For organizing outposts across modules, see Modular Apps.

Released under the MIT License.