Installation
Add the package
Section titled “Add the package”One command installs the package, ejects config/notifications.ts, and scaffolds the in-app model + migration:
npx warlock add notificationsIt also pulls in the mail feature (the package’s one required dependency), so a fresh app is wired end to end. @warlock.js/core and @warlock.js/cascade are already present in a Warlock app.
This ejects into your project — yours to edit:
src/config/notifications.ts— a declarative config wired to the scaffolded model (see below).src/app/notifications/notification.model.ts— extendsDatabaseNotification; declares its physical columns viacolumnMap.src/app/notifications/migrations/…notification.migration.ts—Migration.create(Notification, notificationColumns(Notification)).src/app/notifications/routes.ts+controllers/notifications.controller.ts— the recipient-scoped read/dismiss endpoints (list / unread-count / mark-read / mark-all-read / clear / delete), gated byauthMiddleware. Delete them if your app exposes notifications another way (sockets, GraphQL, …).
Prefer to wire it by hand?
yarn add @warlock.js/notifications(npm / pnpm work too), then create the files yourself. The generator is the recommended path — it keeps them in sync and matches the conventions below.
What config/notifications.ts looks like
Section titled “What config/notifications.ts looks like”The file is declarative: it exports a config object, and Warlock’s notifications connector reads that default export at boot and registers it. You never call setNotificationConfig yourself — the connector does, the same way the rest of src/config/*.ts is wired.
import { type NotificationConfig, mailChannel, inApp } from "@warlock.js/notifications";import { Notification } from "app/notifications/notification.model";
const config: NotificationConfig = { channels: { mail: mailChannel({ from: "no-reply@store.com" }), database: inApp.configure({ model: Notification }), }, // optional gates: // preferences: userPreferences, // rateLimit,};
export default config;inApp.configure({ model }) does double duty: it binds the in-app store and returns the database channel, so reads and writes share one repository.
- Your first notification — define one and fire it end to end.