Controllers & Actions
Controllers are background-context message endpoints. They expose namespaced actions and can dispatch events.
Generate a Controller
hexa generate controller tabs --namespace tabs
@Controller requires a namespace object. Action names are combined as namespace:action during AOT scanning.
@Action must be unique by full route key. You cannot declare two actions that resolve to the same namespace:action path anywhere in the background context.
Background Controller Example
import { Controller, Action } from '@hexajs-dev/core';
import { TabsPort } from '@hexajs-dev/ports';
@Controller({ namespace: 'tabs' })
export class TabsController {
constructor(private tabsPort: TabsPort) {}
@Action('active')
async activeTab(): Promise<{ tabId: number }> {
const tabs = await this.tabsPort.queryTabs({ active: true, currentWindow: true });
return { tabId: tabs[0]?.id ?? -1 };
}
}
Fire-and-Forget Events
Use @On for background events that do not need request-response semantics:
import { Controller, On } from '@hexajs-dev/core';
import { NotificationsPort } from '@hexajs-dev/ports';
@Controller({ namespace: 'audit' })
export class AuditController {
constructor(private notifications: NotificationsPort) {}
@On('high-risk')
async onHighRisk(payload: { title: string; message: string }): Promise<void> {
await this.notifications.create({
type: 'basic',
iconUrl: 'icons/icon-48.png',
title: payload.title,
message: payload.message,
});
}
}
Notes
- Prefer ports (
@hexajs-dev/ports) over directchrome.*orbrowser.*API calls. @Actionis unicast. Only one handler can exist for a givennamespace:actionroute.- Keep controller classes background-focused and delegate reusable logic to services.
- Dispatch store actions from controllers when background state must change.
- Use lifecycle hooks (
onInit/onDestroy) for subscription setup/cleanup. - See Handlers for content-side endpoints, State Management for store usage, and Browser-Agnostic Messaging for routing and ports.