Portal Community

Registry Responsibilities

Registry API

EndpointDescription
GET /api/widget-registryList all registered widget definitions (used by the builder palette)
GET /api/widget-registry/{typeId}Get a specific widget definition including full config schema
POST /api/widget-registry/registerRegister a new custom widget (requires platform admin role)
PUT /api/widget-registry/{typeId}/tenant-overrideSet tenant-level config override for a widget type
DELETE /api/widget-registry/{typeId}Unregister a custom widget (system widgets cannot be deleted)

Registry in the Frontend

The TypeScript WidgetRegistry class manages the client-side catalog:

// WidgetRegistry.ts (simplified)
class WidgetRegistry {
  private definitions = new Map<string, WidgetDefinition>();

  register(def: WidgetDefinition): void {
    this.definitions.set(def.widgetTypeId, def);
  }

  get(typeId: string): WidgetDefinition | undefined {
    return this.definitions.get(typeId);
  }

  getAll(): WidgetDefinition[] {
    return Array.from(this.definitions.values());
  }

  async loadFederatedWidget(typeId: string): Promise<React.ComponentType> {
    const def = this.get(typeId);
    if (!def?.federationUrl) throw new Error(`No federation for ${typeId}`);

    // Dynamically load the remote module via Webpack Module Federation
    const container = await loadRemote(def.federationUrl);
    return container.get(def.federationModule);
  }
}

export const widgetRegistry = new WidgetRegistry();

System Widget Registration

System widgets are registered at application startup in the App Studio bootstrap:

// App Studio bootstrap
import { widgetRegistry } from './WidgetRegistry';
import { DataGridDefinition } from './widgets/DataGrid';
import { ChartDefinition } from './widgets/Chart';
import { MetricDefinition } from './widgets/Metric';
// ... all system widgets

export function bootstrapWidgets(): void {
  widgetRegistry.register(DataGridDefinition);
  widgetRegistry.register(ChartDefinition);
  widgetRegistry.register(MetricDefinition);
  widgetRegistry.register(FormDefinition);
  widgetRegistry.register(ButtonDefinition);
  widgetRegistry.register(TextDefinition);
  // ...
}
Registry Caching

Widget definitions are cached in Redis on the backend (WidgetService.cs) with a long TTL (1 hour). The frontend fetches the list on builder startup and caches it in memory for the session. Changes to custom widget registrations take effect after the next builder session or cache invalidation.