alepha@docs:~/docs/framework/guides/core$
cat 3-internal-events.md | pretty
2 min read
Last commit:

#Internal Events

Alepha uses a hook-based event system for lifecycle management and cross-service communication.

#Confusion Warning

  • $hook is not a React Hook (e.g. useState). It is an event listener.
  • $hook is not a pub/sub system. Use $topic for pub/sub.

#Lifecycle

When alepha.start() is called, the framework emits events in this order:

txt
configure  ->  start  ->  ready  ->  (APP RUNNING)  ->  stop
Hook When Typical use
configure Before start, after container is locked Register providers, resolve configuration
start After configure Connect to databases, start listeners
ready After start Application is fully operational
stop On shutdown (SIGINT/SIGTERM or manual) Close connections, flush buffers

All four receive the Alepha instance as payload.

#Using $hook

Register hooks with the $hook primitive. It must be a class property.

typescript
 1import { $hook } from "alepha"; 2import { $logger } from "alepha/logger"; 3  4class DatabaseService { 5  log = $logger(); 6  7  onStart = $hook({ 8    on: "start", 9    handler: async () => {10      await this.connectToDatabase();11      this.log.info("Database connected");12    },13  });14 15  onStop = $hook({16    on: "stop",17    handler: async () => {18      await this.disconnectFromDatabase();19      this.log.info("Database disconnected");20    },21  });22}

#Hook options

typescript
1$hook({2  on: "start", // required: event name3  handler: async () => {4    /* ... */5  }, // required: callback6  priority: "first", // optional: "first" | "last" (default: insertion order)7});

priority: "first" places the hook at the front of the execution queue. priority: "last" places it at the end. Without a priority, hooks execute in registration order (which follows dependency order). For finer ordering relative to specific services, before and after accept a service class (or an array of them) that this hook must run before or after.

#Hook call tracking

Each $hook instance tracks how many times it has been called:

typescript
1const alepha = Alepha.create().with(App);2await alepha.start();3 4const app = alepha.inject(App);5console.log(app.onStart.called); // 1

This is useful for testing to ensure hooks are called the expected number of times.

#Built-in Hooks

The core Hooks interface defines:

typescript
 1interface Hooks { 2  configure: Alepha; // configuration phase 3  start: Alepha; // start phase 4  ready: Alepha; // ready phase 5  stop: Alepha; // shutdown phase 6  "state:mutate": { 7    // state change notification 8    key: keyof State; 9    value: any;10    prevValue: any;11  };12  "state:register": { atom: Atom }; // an atom was registered13  echo: unknown; // free-form event for testing/debugging14}

Other modules extend this interface. For example, alepha/logger adds log, alepha/server adds server-related hooks, and so on.

#Custom Hooks

Define custom hooks using TypeScript module augmentation:

typescript
1declare module "alepha" {2  interface Hooks {3    "billing:invoice:created": {4      invoiceId: string;5      amount: number;6    };7  }8}

#Listening to custom hooks

As a class property with $hook:

typescript
1class NotificationService {2  onInvoice = $hook({3    on: "billing:invoice:created",4    handler: async ({ invoiceId, amount }) => {5      await this.sendReceipt(invoiceId, amount);6    },7  });8}

Or directly on the event manager:

typescript
1alepha.events.on("billing:invoice:created", ({ invoiceId, amount }) => {2  console.log(`Invoice ${invoiceId} created for ${amount}`);3});

alepha.events.on() returns an unsubscribe function:

typescript
1const unsubscribe = alepha.events.on("billing:invoice:created", handler);2// later...3unsubscribe();

#Emitting custom hooks

typescript
1await alepha.events.emit("billing:invoice:created", {2  invoiceId: "inv_123",3  amount: 99.99,4});

The emit method accepts options:

typescript
1await alepha.events.emit("billing:invoice:created", payload, {2  log: true, // log execution timing of each hook3  catch: true, // catch errors and log them instead of throwing4});

#Compiled Events (advanced)

emit() already compiles and caches an optimized executor per event, so there is no overhead to avoid in normal code. compile() exists to hoist that one cache lookup out of a hot loop:

typescript
1// After all hooks are registered (e.g. after start)2const onRequest = alepha.events.compile("server:onRequest", { catch: true });3 4// In the request handler - returns void if sync, Promise if async5const result = onRequest({ request, route });6if (result) await result;