Skip to content
Warlock.js v4.2.11

Channels & the registry

A channel is one transport with two jobs: resolve where to reach a recipient, and send a rendered payload there.

interface Channel<P> {
name: string;
route?(notifiable): string | { id } | undefined; // the address
send(ctx: { payload: P; route; notifiable?; options }): Promise<void>;
}

Every channel name maps to a payload type in one interface:

interface NotificationChannels {
mail: { subject: string; html?: string; text?: string; cc?: string | string[] };
database: DatabasePayload;
}

This single interface is the declaration-merge target that makes everything type-safe:

  • notify.mail(user, payload)payload is checked against NotificationChannels["mail"].
  • defineNotification({ via, mail, database }) — each renderer’s return type is checked.

A custom channel extends it with three lines, and instantly gets the same typing:

declare module "@warlock.js/notifications" {
interface NotificationChannels {
discord: { content: string };
}
}
// → notify.discord(user, { content: "🎉" }) is now typed

Routing — a channel concern, not a model concern

Section titled “Routing — a channel concern, not a model concern”

There is no routeNotificationFor on your model. Each channel resolves the recipient’s address itself, by convention, overridable in config:

ChannelDefault routeOverride
mailnotifiable.get("email")mailChannel({ route: (n) => n.get("workEmail") })
database{ id: notifiable.id }— (always the id)
customwhatever your route() returnsin the factory

Recipients are cascade Model instances — .id and .get(path) come for free. Pass a raw string instead of a model for a one-off target:

await notify.mail("guest@example.com", { subject: "Receipt", html: "" });

If a channel declares a route() resolver and it returns undefined (e.g. a mail recipient with no email), the dispatch throws UnresolvableRouteError — it will not silently coerce to { id } and hand a string-route channel an object. Only channels that declare no resolver fall back to { id }.

ChannelStatusBacked by
mailshippedcore sendMail
databaseshippedcascade repository
whatsapp / telegram / slack / pushwith @warlock.js/bridgesbridge providers
anything elsedefineChannelyour send()

Until bridges lands, an HTTP channel (Slack/Discord/internal webhook) is a 30-line defineChannel.