Notify
Notification dispatch as a Nuxt module. Feature modules such as Users and Auth dispatch email, SMS or simple notifications without deciding how they are delivered. That choice, and its configuration, belongs to the app.
auth, users,
forms); add it to modules only to configure it.Quick setup
npx nuxt module add @frigear-nu/notify
export default defineNuxtConfig({
modules: ['@frigear-nu/notify'],
notify: {
email: { driver: 'resend' },
sms: { driver: '~/server/utils/twilio-sms-driver' },
simple: { driver: 'log' },
},
})
Options
Configured under notify. Each notification type has its own driver; only the configured driver is loaded.
| Option | Type | Default | Purpose |
|---|---|---|---|
email.driver | 'log' | 'resend' | 'ses' | 'cloudflare' | string | 'log' | A built-in name, or a path to a file whose default export is an EmailDriver. A built-in name only sets the runtime default (see Environment variables). |
sms.driver | 'log' | string | 'log' | 'log', or a path to a file whose default export is an SMSDriver. |
simple.driver | 'log' | string | 'log' | 'log', or a path to a file whose default export is a SimpleDriver. |
push.driver | string | none | Reserved. Typed but not implemented. |
The log driver prints the notification to the console instead of sending it, a safe default for development.
Environment variables
The built-in email drivers use plain fetch (SES requests are signed with AWS SigV4 through Web Crypto), so they have
no dependencies. The driver and its credentials are read at runtime from runtimeConfig.notify.email, so you can
switch providers per environment without rebuilding. The env var wins over notify.email.driver in
nuxt.config.ts. A missing credential fails the send with an error naming the variable to set.
| Variable | Used by | Purpose |
|---|---|---|
NUXT_NOTIFY_EMAIL_DRIVER | all | log (default), resend, ses or cloudflare. Any other value throws at send time. |
NUXT_NOTIFY_EMAIL_FROM | resend, ses, cloudflare | Sender address, for example Frigear <no-reply@frigear.nu>. |
NUXT_NOTIFY_EMAIL_RESEND_API_TOKEN | resend | API token. |
NUXT_NOTIFY_EMAIL_SES_REGION | ses | AWS region, for example eu-west-1. Uses the SES v2 API. |
NUXT_NOTIFY_EMAIL_SES_ACCESS_KEY_ID | ses | Access key id. |
NUXT_NOTIFY_EMAIL_SES_SECRET_ACCESS_KEY | ses | Secret access key. |
NUXT_NOTIFY_EMAIL_SES_SESSION_TOKEN | ses | Optional, for temporary credentials. |
NUXT_NOTIFY_EMAIL_CLOUDFLARE_ACCOUNT_ID | cloudflare | Cloudflare account id (Email Service). |
NUXT_NOTIFY_EMAIL_CLOUDFLARE_API_TOKEN | cloudflare | API token. |
Server
Dispatch functions and definers are auto-imported in server code wherever the module is installed.
| Function | Signature |
|---|---|
dispatchEmailNotification | (notification: EmailNotification) => Promise<void> |
dispatchSMSNotification | (notification: SMSNotification) => Promise<void> |
dispatchSimpleNotification | (notification: SimpleNotification) => Promise<void> |
defineEmailNotification, defineSMSNotification, defineSimpleNotification | Typed identity helpers that return the notification unchanged. |
defineEmailDriver, defineSMSDriver, defineSimpleDriver | Typed identity helpers that return the driver unchanged. |
await dispatchEmailNotification({
to: user.email,
subject: 'Welcome!',
body: '<p>Thanks for signing up.</p>',
text: 'Thanks for signing up.',
})
await dispatchSMSNotification({
to: user.phone,
body: 'Your verification code is 123456.',
})
await dispatchSimpleNotification({
title: 'New signup',
text: `${user.email} just signed up.`,
})
Templates
Notify does not render templates itself. dispatchEmailNotification calls the notify:email Nitro hook before the
driver runs, and listeners may replace ctx.notification. Email Templates uses it to
render notification.template with Vue. Without a renderer the notification is sent as built, so subject and body
are always the fallback.
| Hook | When | Payload |
|---|---|---|
notify:email (Nitro) | An email is about to be handed to the driver | { notification }, replaceable |
notify:email:templates (Nuxt) | A renderer collects the template directories modules ship | EmailTemplateSource[]: push { dir } |
In-app toasts are not part of this module: use useToast() from Nuxt UI.
Types
Exported from @frigear-nu/notify/types. The root package also re-exports the notification, driver and
ModuleOptions types.
interface EmailNotification<M = NotificationMeta> {
to: string | string[]
subject: string
/** HTML body. */
body: string
/** Optional plain-text body. */
text?: string
/** Address replies go to (the `Reply-To` header). */
replyTo?: string
/** Template to render instead of subject/body/text, when a renderer is installed. */
template?: { name: string, props?: Record<string, unknown>, locale?: string }
meta?: M
}
interface SMSNotification<M = NotificationMeta> {
to: string | string[]
body: string
meta?: M
}
interface SimpleNotification<M = Record<string, unknown>> {
title: string
text: string
/** Optional URL to open when the notification is clicked. */
url?: string
meta?: M
}
type NotificationMeta<AdditionalMeta = Record<string, unknown>> = { driver?: string } & AdditionalMeta
type EmailDriver = (notification: EmailNotification) => Promise<void> | void
type SMSDriver = (notification: SMSNotification) => Promise<void> | void
type SimpleDriver = (notification: SimpleNotification) => Promise<void> | void
Custom driver
A driver is a function that delivers one notification. Point the driver option at a file whose default export is that function:
// server/utils/resend-email-driver.ts
import type { EmailDriver } from '@frigear-nu/notify/types'
const sendWithResend: EmailDriver = async (notification) => {
// call your provider with notification.to / subject / body / text / replyTo
}
export default sendWithResend
Store types
@frigear-nu/notify/types also exports StoredNotification and NotificationStore, a small persistence contract:
type StoredNotification = {
id: string
type: 'email' | 'sms' | 'simple' | string
data: EmailNotification | SMSNotification | SimpleNotification
error?: string
sentAt?: Date
createdAt: Date
updatedAt?: Date
}
type NotificationStore<S = StoredNotification> = {
put(notification: S): Promise<void>
get(id: string): Promise<S | null>
list(): Promise<S[]>
delete(id: string): Promise<void>
}
Nothing in the module uses a store yet: dispatch does not record notifications.