devclassic-notify
v1.0.2
Published
Enterprise Multi-Channel Notification Engine (Email, SMS, WhatsApp, Push) for React, Next.js, and Node.js by devclassic.
Maintainers
Readme
devclassic-notify
Enterprise Multi-Channel Notification Engine for Modern Web & Mobile Applications
Unify Email, SMS, WhatsApp, and Push notifications under a single, elegant API with automatic failover and seamless frontend/backend integration.
💡 Use It Your Way (Standalone or Full-Stack)
devclassic-notify is completely modular. You can use it only on the backend, only on the frontend, or both together:
| Usage Mode | Where It Runs | What You Use | Do You Need React? |
| :--- | :--- | :--- | :--- |
| 🖥️ Backend-Only | Node.js, Express, NestJS, Fastify, Serverless | createNotificationEngine, SMS, Email, WhatsApp, Push providers | No (React is an optional peer dependency) |
| 🎨 Frontend-Only | React, Vite, Next.js client, CRA | <NotificationBell />, <NotificationPreferences />, useWebPush() | Yes (Connects to any custom API: Python, Go, PHP, etc.) |
| ⚡ Full-Stack | Next.js (App Router), Remix | Backend dispatching + Frontend In-App notification center | Yes (Unified end-to-end) |
📖 Table of Contents
- ✨ Core Features
- 📦 Installation
- 🚀 Quickstart by Use Case
- 📡 Multi-Channel Dispatch API
- 🛡️ Automatic Provider Failover
- 📝 Dynamic Variable Templates (
{{variables}}) - 👤 User Channel Preferences & Opt-Outs
- 🔔 Frontend React Components & Hooks
- 🔑 How to Get VAPID Keys / Valid ID (Web Push)
- 🆔 Provider Valid IDs & Setup Guide
- 🔌 Supported Providers Reference
- 🧪 Offline Sandbox & Mocking
- 📚 Guides & Complete Documentation
- 📄 License
✨ Core Features
- 🌐 Unified Multi-Channel API: Dispatch across Email, SMS, WhatsApp, and Push through one function call.
- 🛡️ Zero-Drop Failover: Automatically retries secondary providers if your primary provider encounters rate limits or 5xx outages.
- 🌍 African & Global Providers: Zero-heavy-SDK HTTP adapters for Gmail, Termii (Nigeria/Africa SMS & WhatsApp), Twilio, Resend, SendGrid, Meta Cloud API, FCM, and Expo.
- 🎨 Ready-to-Use React UI: Drop-in
<NotificationBell />,<NotificationPreferences />settings card, anduseWebPush()browser permission helper. - 📝 Smart Templating: Dot-notation nested variable interpolation (
{{user.name}},{{order.id}}). - 👤 Channel Preferences: Automatically respects recipient channel opt-outs (e.g. user toggles OFF SMS but keeps WhatsApp ON).
- 🧪 Offline Mocking: Built-in test adapters for CI/CD and local development without spending real API credits.
- 📦 Dual Build: CommonJS (
.cjs) + ESM (.mjs) + Full TypeScript definitions (.d.ts).
📦 Installation
npm install devclassic-notify
# or
yarn add devclassic-notify
# or
pnpm add devclassic-notifyNote on React: If you are using
devclassic-notifyexclusively on the backend (Node.js/Express), you do not need to install React. React is defined as an optional peer dependency.
🚀 Quickstart by Use Case
Scenario A: Backend-Only (Node.js / Express / APIs)
No React required. Works with Express, NestJS, Fastify, AWS Lambda, etc.
// notify.ts
import {
createNotificationEngine,
resendProvider,
termiiSMSProvider,
metaWhatsAppProvider,
expoPushProvider,
} from "devclassic-notify";
// 1. Initialize the engine with your providers
export const notify = createNotificationEngine({
channels: {
email: resendProvider({
apiKey: process.env.RESEND_API_KEY!,
from: "Alerts <[email protected]>",
}),
sms: termiiSMSProvider({
apiKey: process.env.TERMII_API_KEY!,
from: "YOURAPP", // Approved Sender ID
}),
whatsapp: metaWhatsAppProvider({
accessToken: process.env.META_WA_TOKEN!,
phoneNumberId: process.env.META_WA_PHONE_ID!,
}),
push: expoPushProvider(),
},
});
// 2. Dispatch to one or multiple channels simultaneously
const result = await notify.send({
to: {
email: "[email protected]",
phone: "+2348012345678",
whatsapp: "+2348012345678",
pushToken: "ExponentPushToken[xxxxxxxxxxxxxx]",
},
channels: ["email", "sms", "whatsapp", "push"],
subject: "Order Confirmation #8491",
content: "Hello {{name}}, your order for {{item}} has been confirmed!",
data: { name: "Amina", item: "Solar Generator" },
});
console.log("Success:", result.success);
console.log("Channel results:", result.results);Scenario B: Frontend-Only (React / Vite / Single Page Apps)
Use only the UI components and push hook in your React app, connecting to ANY backend (Django, Laravel, Rails, Go, etc.).
// App.tsx
import React from "react";
import {
NotificationProvider,
NotificationBell,
NotificationPreferences,
useWebPush,
} from "devclassic-notify";
export function App() {
// 1. Browser Web Push Hook
const { isSupported, isSubscribed, subscribe } = useWebPush({
vapidPublicKey: process.env.NEXT_PUBLIC_VAPID_PUBLIC_KEY,
onSubscribe: async (subscription) => {
// Send push token to YOUR custom backend database
await fetch("https://api.yourdomain.com/push-tokens", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ subscription }),
});
},
});
return (
<NotificationProvider>
<header className="navbar">
<h2>Dashboard</h2>
{/* 2. Notification Bell with Unread Badge & Dropdown Feed */}
<NotificationBell />
{/* Push notification opt-in button */}
{!isSubscribed && (
<button onClick={subscribe}>Enable Browser Notifications</button>
)}
</header>
<main>
{/* 3. User Channel Preferences Settings Card */}
<NotificationPreferences
onChange={async (preferences) => {
// Save preferences (e.g. { email: true, sms: false, whatsapp: true })
await fetch("https://api.yourdomain.com/user/preferences", {
method: "POST",
body: JSON.stringify(preferences),
});
}}
/>
</main>
</NotificationProvider>
);
}Scenario C: Full-Stack (Next.js App Router)
In a Next.js full-stack application, configure the engine in a server file and use the React components on the client:
- Server-Side (
app/notify.server.ts): Initialize engine with Resend, Termii, Meta WhatsApp, FCM/Expo. - Client-Side (
app/layout.tsx): Wrap your application in<NotificationProvider>. - Navbar Component (
app/components/Navbar.tsx): Render<NotificationBell />anduseWebPush(). - Route Handler (
app/api/notify/route.ts): Callnotify.send()from Server Actions or Route Handlers.
👉 See Complete Next.js Full-Stack App Router Guide for a full step-by-step walkthrough.
📡 Multi-Channel Dispatch API
The notify.send() method is the core dispatch interface:
const result = await notify.send({
// Recipient info (single string phone/email or structured object)
to: {
email: "[email protected]",
phone: "+2348012345678",
whatsapp: "+2348012345678",
pushToken: "ExponentPushToken[xxxxxxxxxxxxxx]",
// Optional: Recipient preferences to automatically filter channels
preferences: {
email: true,
whatsapp: true,
sms: false, // SMS will automatically be skipped for this user
push: true,
},
},
// Channels to dispatch to
channels: ["email", "whatsapp", "sms", "push"],
// Message content (supports {{variables}})
subject: "Audit Notification: {{facilityName}}",
content: "Hello {{name}}, your audit scheduled for {{date}} has been approved.",
// Template variables
data: {
name: "Dr. Aliyu",
facilityName: "National Hospital",
date: "March 15, 2026",
},
// Priority flag
priority: "high", // "high" | "normal" | "low"
});🛡️ Automatic Provider Failover
Never miss an urgent alert due to a 3rd-party provider downtime, network timeout, or rate limit:
import {
createNotificationEngine,
resendProvider,
sendgridProvider,
termiiSMSProvider,
twilioSMSProvider,
} from "devclassic-notify";
export const notify = createNotificationEngine({
channels: {
// 📧 Email Failover: Try Resend first, fall back to SendGrid
email: {
primary: resendProvider({ apiKey: process.env.RESEND_API_KEY! }),
fallbacks: [
sendgridProvider({ apiKey: process.env.SENDGRID_API_KEY! }),
],
},
// 📱 SMS Failover: Try Termii (Africa), fall back to Twilio (Global)
sms: {
primary: termiiSMSProvider({
apiKey: process.env.TERMII_API_KEY!,
from: "YOURAPP",
}),
fallbacks: [
twilioSMSProvider({
accountSid: process.env.TWILIO_ACCOUNT_SID!,
authToken: process.env.TWILIO_AUTH_TOKEN!,
from: "+14155552671",
}),
],
},
},
});📝 Dynamic Variable Templates ({{variables}})
Pre-register templates with dot-notation variable interpolation:
const notify = createNotificationEngine({
templates: {
"otp-verification": {
content: "Your verification code for {{appName}} is {{code}}. Valid for 10 minutes.",
},
"order-status": {
subject: "Order {{order.id}} Update",
content: "Hi {{user.name}}, your item {{order.item}} is now {{order.status}}.",
},
},
});
// Dispatch using template key
await notify.send({
to: "+2348012345678",
template: "otp-verification",
data: { appName: "PayGuard", code: "938201" },
});👤 User Channel Preferences & Opt-Outs
Respecting user communication choices is built into the engine. When a recipient has channel preferences, notify.send() automatically skips channels that are disabled:
await notify.send({
to: {
email: "[email protected]",
phone: "+2348012345678",
preferences: {
email: true,
sms: false, // Skipped!
whatsapp: true, // Sent!
},
},
channels: ["email", "sms", "whatsapp"],
content: "Important account alert.",
});🔔 Frontend React Components & Hooks
1. <NotificationBell /> In-App Center
An interactive bell icon with an unread badge counter and dropdown feed:
import { NotificationProvider, NotificationBell } from "devclassic-notify";
export function Header() {
return (
<NotificationProvider
initialNotifications={[
{
id: "1",
title: "Inspection Approved",
message: "Facility audit was approved successfully.",
read: false,
createdAt: new Date(),
},
]}
>
<header className="flex justify-between items-center p-4">
<h1>Dashboard</h1>
<NotificationBell />
</header>
</NotificationProvider>
);
}2. <NotificationPreferences /> User Toggles
Pre-styled card allowing users to toggle their preferred channels:
import { NotificationPreferences } from "devclassic-notify";
export function SettingsPage() {
const handleSave = async (preferences) => {
// preferences = { email: true, sms: false, whatsapp: true, push: true }
await api.savePreferences(preferences);
};
return (
<div className="max-w-md p-4">
<h3>Notification Preferences</h3>
<NotificationPreferences onChange={handleSave} />
</div>
);
}3. useWebPush() Browser Push Hook
One-click hook to request browser push permissions and retrieve a subscription token:
import { useWebPush } from "devclassic-notify";
export function PushSubscribeButton() {
const { isSupported, isSubscribed, subscribe } = useWebPush({
vapidPublicKey: process.env.NEXT_PUBLIC_VAPID_PUBLIC_KEY,
onSubscribe: async (subscription) => {
// Save subscription to backend database
await fetch("/api/push/subscribe", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ subscription }),
});
},
onError: (err) => console.error("Push subscription failed:", err),
});
if (!isSupported) return null;
return (
<button onClick={subscribe} disabled={isSubscribed}>
{isSubscribed ? "Push Notifications Active" : "Enable Push Notifications"}
</button>
);
}🔑 How to Get VAPID Keys / Valid ID (Web Push)
What is VAPID / Valid ID?
VAPID (Voluntary Application Server Identification, RFC 8292) is the web push security standard. Browsers (Chrome, Firefox, Safari, Edge) require a valid Public VAPID Key (applicationServerKey) to ensure push notifications originate from your verified application server.
Step 1: Generate Your VAPID Key Pair (5 Seconds)
Run this command in your terminal using npx:
npx web-push generate-vapid-keysOutput:
=======================================
Public Key:
BEl62iUYgUivxIkv69yViEuiBIa-Ib9-SkvMeAtA3LFgDZKrxZJjS5Snfflj004GTRUMMVM4zV123...
Private Key:
_87KM2EZxxK...
=======================================(Alternatively, run npm i web-push and call webpush.generateVAPIDKeys() in Node.js).
Step 2: Set Environment Variables
Add the keys to your .env.local or .env file:
# Frontend (Client-accessible): Used by useWebPush() hook
NEXT_PUBLIC_VAPID_PUBLIC_KEY=BEl62iUYgUivxIkv69yViEuiBIa-Ib9-SkvMeAtA3LFgDZKrxZJjS5Snfflj004GTRUMMVM4zV123...
# Backend (Keep private!): Used by your server to sign push dispatches
VAPID_PRIVATE_KEY=_87KM2EZxxK...
VAPID_SUBJECT=mailto:[email protected][!TIP] In Next.js, always prefix client-side variables with
NEXT_PUBLIC_(orVITE_in Vite apps). Never expose theVAPID_PRIVATE_KEYon the client.
🆔 Provider Valid IDs & Setup Guide
Different providers require specific valid IDs and credentials to dispatch notifications:
| Channel / Provider | Required Valid ID | Purpose & Where to Obtain |
| :--- | :--- | :--- |
| Web Push | vapidPublicKey | Authenticates your app with browser push services. Generated via npx web-push generate-vapid-keys. |
| Termii (SMS) | Valid from (Approved Sender ID) | Registered alphanumeric sender name (e.g. YOURAPP) approved by telecom regulators (e.g. NCC in Nigeria) on the Termii Dashboard. |
| Twilio (SMS / WA) | Valid from phone number | Approved Twilio phone number (e.g. +14155552671 or whatsapp:+14155238886) from the Twilio Console. |
| Meta WhatsApp | phoneNumberId (Valid Phone ID) | 15-digit unique ID identifying your WhatsApp Business Phone Number on the Meta for Developers Portal. |
| Firebase (FCM) | serverKey | Legacy Cloud Messaging server key from the Firebase Console Project Settings. |
| Expo (Push) | accessToken (optional) | Personal Access Token from Expo Settings for high-throughput mobile push. |
How to Obtain Key Provider IDs:
1. Termii Approved Sender ID (SMS)
Regulatory frameworks (e.g. NCC in Nigeria) require SMS messages to be sent from an approved Sender ID:
- Log in to your Termii Dashboard.
- In the sidebar, click Sender ID.
- Click Request Sender ID, enter your alphanumeric name (3 to 11 characters, e.g.
YOURAPP), and submit a sample notification. - Once approved, use that exact name:
termiiSMSProvider({ apiKey: process.env.TERMII_API_KEY!, from: "YOURAPP", // Valid, approved Sender ID })
2. Meta WhatsApp Phone Number ID
The phoneNumberId is the unique ID assigned to your WhatsApp Business phone number:
- Navigate to the Meta for Developers Portal and select your App.
- In the sidebar, go to WhatsApp > API Setup.
- Under Step 1: Select phone numbers, copy the numeric Phone number ID:
metaWhatsAppProvider({ accessToken: process.env.META_WA_TOKEN!, phoneNumberId: "109283746501928", // Valid Phone Number ID })
🔌 Supported Providers Reference
All providers use lightweight HTTP requests with zero heavy SDK dependencies:
| Channel | Built-in Providers | Key Characteristics |
| :--- | :--- | :--- |
| 📧 Email | resendProvidersendgridProvidergmailProvidermockEmailProvider | Zero-dependency fetch, HTML/Text support, Google OAuth2/Gmail REST API, failover support. |
| 📱 SMS | termiiSMSProvidertwilioSMSProvidermockSMSProvider | Specialized for Nigeria/Africa (Termii) and Global delivery (Twilio). |
| 💬 WhatsApp | metaWhatsAppProvidertwilioWhatsAppProvidermockWhatsAppProvider | Direct Meta Cloud Graph API & Twilio WhatsApp Sandbox / production. |
| 🔔 Push | expoPushProviderfcmPushProvidermockPushProvider | Mobile React Native (Expo), Firebase Cloud Messaging (FCM), & Web Push. |
🧪 Offline Sandbox & Mocking
Test notifications in CI/CD or local development without spending real API credits or sending actual messages:
import {
mockEmailProvider,
mockSMSProvider,
mockWhatsAppProvider,
mockPushProvider,
} from "devclassic-notify";
const emailMock = mockEmailProvider({ logToConsole: true });
const smsMock = mockSMSProvider({ logToConsole: true });
// Inspect sent messages in test assertions:
const sent = emailMock.getSentEmails();
console.log("Sent recipient:", sent[0].payload.recipient.email);📚 Guides & Complete Documentation
- 🚀 Next.js Full-Stack App Router Guide: End-to-end tutorial with layout, navbar, server routes, and push buttons.
- 🔌 Multi-Channel Provider Configuration Guide: In-depth setup for Termii, Twilio, Resend, Meta WhatsApp, and Expo.
📄 License
MIT © devclassic
