npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@atlex/notifications

v0.1.5

Published

Notification channels and database notifications for Atlex

Readme

@atlex/notifications

Multi-channel notifications: mail, Slack, and database.

npm TypeScript

Buy Me A Coffee

Installation

npm install @atlex/notifications
# or
yarn add @atlex/notifications

Quick Start

import { Notification, MailChannel, MailMessage } from '@atlex/notifications'

class OrderShippedNotification extends Notification {
  constructor(private order: any) {
    super()
  }

  via() {
    return ['mail']
  }

  toMail() {
    return new MailMessage()
      .subject(`Order ${this.order.id} has shipped`)
      .greeting('Hello!')
      .line('Your order is on the way.')
      .action('Track Order', `https://example.com/orders/${this.order.id}`)
      .line('Thank you for your purchase!')
  }
}

// Send notification
const user = { email: '[email protected]' }
await user.notify(new OrderShippedNotification(order))

Features

  • Multi-Channel Delivery: Send via mail, Slack, database, or custom channels
  • Fluent API: Build notifications with a clean, expressive interface
  • Delayed Delivery: Schedule notifications to be sent later
  • Conditional Routing: Decide delivery channels based on notification content
  • Broadcast Notifications: Send to multiple channels simultaneously
  • Notifiable Models: Add notification support to any model
  • Event Hooks: Listen to notification lifecycle events
  • Queue Integration: Async notification delivery with job queues

Creating Notifications

Basic Notification Class

import { Notification, MailMessage, DatabaseMessage } from '@atlex/notifications'

class WelcomeNotification extends Notification {
  constructor(private userName: string) {
    super()
  }

  // Define which channels to use
  via() {
    return ['mail', 'database']
  }

  // Build mail notification
  toMail() {
    return new MailMessage()
      .subject('Welcome to Our Platform')
      .greeting(`Hello ${this.userName}!`)
      .line('Thanks for joining us.')
      .action('Get Started', 'https://example.com/setup')
      .line('We are excited to have you on board!')
  }

  // Build database notification
  toDatabase() {
    return new DatabaseMessage()
      .title('Welcome to Our Platform')
      .message(`Hello ${this.userName}, thanks for joining us!`)
  }
}

Notification Channels

Mail Channel

import { MailMessage } from '@atlex/notifications'

class PaymentConfirmedNotification extends Notification {
  constructor(private amount: number) {
    super()
  }

  via() {
    return ['mail']
  }

  toMail() {
    return new MailMessage()
      .subject(`Payment of $${this.amount} confirmed`)
      .greeting('Hello!')
      .line(`We have received your payment of $${this.amount}.`)
      .line(`Transaction ID: TXN-${Date.now()}`)
      .line('Thank you!')
      .salutation('Best regards,\nThe Team')
  }
}

Database Channel

import { DatabaseMessage } from '@atlex/notifications'

class CommentReplyNotification extends Notification {
  constructor(
    private comment: any,
    private author: string,
  ) {
    super()
  }

  via() {
    return ['database']
  }

  toDatabase() {
    return new DatabaseMessage()
      .title('New reply to your comment')
      .message(`${this.author} replied: "${this.comment.text}"`)
      .action('View Comment', `/comments/${this.comment.id}`)
  }
}

Slack Channel

import { SlackMessage, SlackAttachment } from '@atlex/notifications'

class DeploymentFailedNotification extends Notification {
  constructor(
    private deployment: any,
    private error: string,
  ) {
    super()
  }

  via() {
    return ['slack']
  }

  toSlack() {
    return new SlackMessage().text('Deployment failed!').attachment(
      new SlackAttachment()
        .title('Deployment Status')
        .fields({
          Environment: this.deployment.environment,
          Branch: this.deployment.branch,
          Error: this.error,
        })
        .markdown(),
    )
  }
}

Notifiable Models

Adding Notification Support

import { Notifiable } from '@atlex/notifications'

class User extends Notifiable {
  id: number
  email: string
  name: string

  // Implement routing for notifications
  routeNotificationFor(channel: string) {
    if (channel === 'mail') {
      return this.email
    }
    if (channel === 'slack') {
      return this.slackUserId // Custom property
    }
    return null
  }

  // Define preferred notification channels
  preferredChannels() {
    return ['mail', 'database']
  }
}

Sending Notifications

const user = await User.find(1)

// Send single notification
await user.notify(new WelcomeNotification(user.name))

// Send and wait for queue
await user.notifyNow(new PaymentConfirmedNotification(100))

// Send to multiple recipients
const users = await User.all()
for (const user of users) {
  await user.notify(new NewsletterNotification())
}

Conditional Routing

import { Notification } from '@atlex/notifications'

class SecurityAlertNotification extends Notification {
  constructor(private severity: 'low' | 'medium' | 'high') {
    super()
  }

  via(notifiable: any) {
    // Route based on severity
    if (this.severity === 'high') {
      return ['mail', 'slack']
    }
    return ['database']
  }

  shouldSend(notifiable: any) {
    // Check if user wants to receive this type
    return notifiable.notificationPreferences?.securityAlerts ?? true
  }
}

Delayed Notifications

import { Notification } from '@atlex/notifications'

class ReminderNotification extends Notification {
  via() {
    return ['mail']
  }

  deliveryDelay() {
    // Delay 24 hours
    return 24 * 60 * 60 * 1000
  }

  toMail() {
    return new MailMessage()
      .subject('Your appointment is tomorrow')
      .line('This is a friendly reminder...')
  }
}

// Schedule notification with delay
await user.notify(new ReminderNotification())

Broadcast Notifications

import { AnonymousNotifiable } from '@atlex/notifications'

// Send to multiple recipients without model binding
const notifiable = new AnonymousNotifiable()
  .route('mail', '[email protected]')
  .route('mail', '[email protected]')
  .route('slack', 'slack-channel-id')

await notifiable.notify(new SystemAnnouncementNotification())

Notification Events

import { NotificationSending, NotificationSent, NotificationFailed } from '@atlex/notifications'
import { EventEmitter } from '@atlex/events'

const emitter = new EventEmitter()

// Listen to sending event
emitter.on('notification.sending', (event: NotificationSending) => {
  console.log(`Sending ${event.notification.constructor.name}`)
})

// Listen to sent event
emitter.on('notification.sent', (event: NotificationSent) => {
  console.log(`Notification sent successfully`)
})

// Listen to failed event
emitter.on('notification.failed', (event: NotificationFailed) => {
  console.error(`Notification failed: ${event.error}`)
})

Database Notifications

DatabaseNotification Model

import { DatabaseNotification } from '@atlex/notifications'

// Retrieve notifications for a user
const notifications = await DatabaseNotification.where('notifiable_id', userId)
  .where('notifiable_type', 'User')
  .get()

// Mark as read
await notification.markAsRead()

// Mark all as read
await DatabaseNotification.where('notifiable_id', userId).update({ readAt: new Date() })

Notification Data Structure

const notification = {
  id: 'uuid',
  notifiable_id: 1,
  notifiable_type: 'User',
  type: 'OrderShipped',
  data: {
    orderId: 123,
    trackingNumber: 'TRACK123',
  },
  readAt: null,
  createdAt: new Date(),
  updatedAt: new Date(),
}

NotificationManager Configuration

import { NotificationManager } from '@atlex/notifications'
import { MailChannel, DatabaseChannel, SlackChannel } from '@atlex/notifications'

const manager = new NotificationManager()

// Register mail channel
manager.channel(
  'mail',
  new MailChannel({
    from: '[email protected]',
    transport: 'smtp',
  }),
)

// Register database channel
manager.channel('database', new DatabaseChannel())

// Register Slack channel
manager.channel(
  'slack',
  new SlackChannel({
    webhook: process.env.SLACK_WEBHOOK_URL,
  }),
)

// Extend with custom channel
manager.extend(
  'sms',
  new SMSChannel({
    apiKey: process.env.SMS_API_KEY,
  }),
)

Complete Example

import {
  Notification,
  MailMessage,
  DatabaseMessage,
  SlackMessage,
  SlackAttachment,
  Notifiable,
} from '@atlex/notifications'

class Order extends Notifiable {
  id: number
  status: string
  userId: number
  total: number

  routeNotificationFor(channel: string) {
    if (channel === 'mail') {
      return this.user.email
    }
    if (channel === 'slack') {
      return this.user.slackId
    }
    return null
  }
}

class OrderStatusChangedNotification extends Notification {
  constructor(
    private order: Order,
    private oldStatus: string,
    private newStatus: string,
  ) {
    super()
  }

  via() {
    return ['mail', 'database', 'slack']
  }

  toMail() {
    return new MailMessage()
      .subject(`Order #${this.order.id} status updated`)
      .greeting(`Hello ${this.order.user.name}!`)
      .line(`Your order status has changed from ${this.oldStatus} to ${this.newStatus}.`)
      .action('View Order', `https://example.com/orders/${this.order.id}`)
      .line('Thank you!')
  }

  toDatabase() {
    return new DatabaseMessage()
      .title(`Order #${this.order.id} ${this.newStatus}`)
      .message(`Your order status changed to ${this.newStatus}.`)
      .action('View Order', `/orders/${this.order.id}`)
  }

  toSlack() {
    return new SlackMessage().text(`Order ${this.order.id} status changed`).attachment(
      new SlackAttachment()
        .title('Order Update')
        .fields({
          'Order ID': `#${this.order.id}`,
          'Previous Status': this.oldStatus,
          'New Status': this.newStatus,
          Total: `$${this.order.total}`,
        })
        .color('#2E7D32'),
    )
  }
}

// Usage
const order = await Order.find(123)
await order.notify(new OrderStatusChangedNotification(order, 'pending', 'shipped'))

API Overview

Notification

| Method | Description | | ---------------------------- | ------------------------- | | via(notifiable?) | Define delivery channels | | toMail() | Build mail message | | toDatabase() | Build database message | | toSlack() | Build Slack message | | toArray() | Convert to array format | | toBroadcast() | Send to multiple channels | | shouldSend(notifiable?) | Check if should send | | deliveryDelay(notifiable?) | Get delivery delay |

Notifiable

| Method | Description | | ------------------------------- | ------------------------------ | | notify(notification) | Send notification | | notifyNow(notification) | Send immediately without queue | | routeNotificationFor(channel) | Get notification route |

Channels

| Channel | Description | | ----------------- | ------------------- | | MailChannel | Email notifications | | DatabaseChannel | Store in database | | SlackChannel | Send to Slack |

Documentation

For complete documentation, visit https://atlex.dev/guide/notifications

License

MIT

Part of Atlex — A modern framework for Node.js.