# Dynamic Routing Reference Pattern

## Architecture Principle

Navigation and permissions are **entirely driven by the database**. The React frontend contains **zero hardcoded routes**. The backend API returns routes already filtered by the user's JWT/session.

## 5 Mandatory Elements

### 1. componentRegistry

**Role**: Static mapping `string -> lazy(component)`. The ONLY allowed static file.

**Requirements**:
- All page imports MUST be lazy (`React.lazy(() => import(...))`)
- Keys are string identifiers (dot-separated hierarchical recommended)
- No business logic in the registry — pure mapping only
- Must be loaded at app startup (imported in main.tsx or App.tsx)
- New pages are registered here and ONLY here

**SmartStack specifics**:
- File: `componentRegistry.generated.ts`
- Registration via `PageRegistry.register()` (v3.7+)
- `PAGE_KEYS` constant for type-safe key references
- Generated by MCP `scaffold_routes` tool

### 2. ProtectedRoute

**Role**: Front-end guard that validates permissions received from the API (defense in depth).

**Requirements**:
- Receives permission requirements from route config (NOT hardcoded)
- Checks user permissions against route requirements
- Redirects unauthorized users (to login or forbidden page)
- Does NOT contain a list of routes or paths
- Permissions come from the API, not from static constants

**SmartStack specifics**:
- File: `ProtectedRoute.tsx`
- Replaced the old `RouteGuard.tsx` (which had hardcoded lists)
- Uses `permissionPath` from menu API DTOs

### 3. DynamicRouter

**Role**: Rendering engine that generates `<Route>` elements from the API configuration.

**Requirements**:
- Iterates over API route config to create React Router `<Route>` elements
- Resolves components from componentRegistry by key
- Wraps routes with ProtectedRoute for permission checking
- Handles nested routes (outlets, tabs)
- Supports loading states (shows loader while config is being fetched)
- No hardcoded route paths in the router itself

**SmartStack specifics**:
- File: `DynamicRouter.tsx`
- Sole routing engine — App.tsx reduced to providers + DynamicRouter
- Handles: dynamic app routes, feature gating, auto-redirects, outlet tab routes, doc routes
- `OUTLET_SECTIONS` config for tab-based pages
- `getStaticAppRoutes()` for per-app legacy redirects (migration period only)

### 4. useRouteConfig

**Role**: Hook that fetches route configuration + permissions from the API at mount.

**Requirements**:
- Calls navigation API on mount (or auth change)
- Returns structured route config with components, paths, and permissions
- Provides loading/error states
- Caches appropriately (invalidates on auth change)
- Flattens hierarchical menu into usable route entries

**SmartStack specifics**:
- File: `useRouteConfig.ts`
- Flattens menu API response into `RouteConfigEntry[]`
- Provides `byKey` and `byApplication` maps
- Used by DynamicRouter to build routes

### 5. Navigation API

**Role**: Endpoint returning routes already filtered by JWT/session.

**Requirements**:
- Returns routes with: path, componentKey, permissions, children
- Filters routes based on authenticated user's permissions
- Hierarchical structure (Application > Module > Section > Resource)
- Includes permission paths for front-end guard
- Supports feature gating (license-based)

**SmartStack specifics**:
- Endpoint: `GET /api/navigation/menu`
- Returns enriched DTOs at 4 levels (Application, Module, Section, Resource)
- `ComponentKey` and `PermissionPath` on all 4 DTO levels
- `RequiredFeature` for license-based feature gating
- Built by `NavigationService.BuildApplicationMenu/BuildModuleMenu/BuildSectionMenu/BuildResourceMenu`

## Expected API Response Structure

```json
{
  "routes": [
    {
      "path": "/administration/users",
      "componentKey": "administration.users.list",
      "permissionPath": "administration.users",
      "permissions": ["administration.users.access", "administration.users.read"],
      "children": [
        {
          "path": ":id",
          "componentKey": "administration.users.detail",
          "permissions": ["administration.users.read"]
        }
      ]
    }
  ]
}
```

## Common Anti-Patterns

| Anti-Pattern | Description | Risk |
|-------------|-------------|------|
| Hardcoded routes in JSX | `<Route path="/admin" ...>` outside DynamicRouter | Routes drift from DB, permissions bypassed |
| Static permission lists | `const ADMIN_PERMS = [...]` in components | Permissions out of sync with API |
| Non-lazy imports in registry | `import Page from './Page'` (eager) | Bundle bloat, slow initial load |
| Missing Suspense boundary | No `<Suspense>` around lazy components | Crashes on slow loads |
| Route paths in components | `navigate('/admin/users')` with hardcoded paths | Tight coupling, breaks if DB paths change |
| Permission checks without API | `if (user.role === 'admin')` | Bypasses permission system |
| Duplicate route definitions | Routes in both DynamicRouter and static config | Conflicts, unpredictable behavior |
| Missing loading state | No loader while route config is fetching | Flash of "not found" page |
