Modules

Notify

Email, SMS and generic notification dispatch with pluggable drivers, shared across Frigear modules.

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.

Requires nothing else. Installs automatically when a module that depends on it is used (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.

OptionTypeDefaultPurpose
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.driverstringnoneReserved. 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.

VariableUsed byPurpose
NUXT_NOTIFY_EMAIL_DRIVERalllog (default), resend, ses or cloudflare. Any other value throws at send time.
NUXT_NOTIFY_EMAIL_FROMresend, ses, cloudflareSender address, for example Frigear <no-reply@frigear.nu>.
NUXT_NOTIFY_EMAIL_RESEND_API_TOKENresendAPI token.
NUXT_NOTIFY_EMAIL_SES_REGIONsesAWS region, for example eu-west-1. Uses the SES v2 API.
NUXT_NOTIFY_EMAIL_SES_ACCESS_KEY_IDsesAccess key id.
NUXT_NOTIFY_EMAIL_SES_SECRET_ACCESS_KEYsesSecret access key.
NUXT_NOTIFY_EMAIL_SES_SESSION_TOKENsesOptional, for temporary credentials.
NUXT_NOTIFY_EMAIL_CLOUDFLARE_ACCOUNT_IDcloudflareCloudflare account id (Email Service).
NUXT_NOTIFY_EMAIL_CLOUDFLARE_API_TOKENcloudflareAPI token.

Server

Dispatch functions and definers are auto-imported in server code wherever the module is installed.

FunctionSignature
dispatchEmailNotification(notification: EmailNotification) => Promise<void>
dispatchSMSNotification(notification: SMSNotification) => Promise<void>
dispatchSimpleNotification(notification: SimpleNotification) => Promise<void>
defineEmailNotification, defineSMSNotification, defineSimpleNotificationTyped identity helpers that return the notification unchanged.
defineEmailDriver, defineSMSDriver, defineSimpleDriverTyped 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.

HookWhenPayload
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 shipEmailTemplateSource[]: 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.

Copyright © 2026