@webdevarif/notifications
v1.0.0
Published
A customizable, framework-agnostic notifications system with pluggable storage and WebSocket adapters
Maintainers
Readme
@webdevarif/notifications
A customizable, framework-agnostic notifications system with pluggable storage and WebSocket adapters.
🚀 Features
- Framework Agnostic: Works with any JavaScript/TypeScript framework
- Pluggable Storage: Use any database with custom storage adapters
- Real-time Updates: WebSocket support with multiple adapter options
- React Integration: Built-in React hooks for easy frontend integration
- TypeScript: Full TypeScript support with comprehensive type definitions
- Customizable: Extend notification types and behaviors
- Event-driven: Lifecycle hooks and event system
- Production Ready: Battle-tested patterns and error handling
📦 Installation
npm install @webdevarif/notifications🏗️ Architecture
The package is designed with a modular architecture:
- Core Service:
Notificationsclass handles business logic - Storage Adapters: Pluggable storage implementations
- WebSocket Adapters: Real-time communication adapters
- Client SDK: Browser/Node.js client for API communication
- React Hooks: React integration for frontend applications
🚀 Quick Start
Server-side Setup
import { Notifications, InMemoryStorageAdapter } from '@webdevarif/notifications';
// Create storage adapter (in-memory for this example)
const storageAdapter = new InMemoryStorageAdapter();
// Create notifications service
const notifications = new Notifications({
storageAdapter,
defaultType: 'info',
});
// Create a notification
const notification = await notifications.create({
recipientId: 'user-123',
type: 'order',
title: 'Order Shipped',
body: 'Your order #12345 has been shipped',
data: { orderId: '12345', trackingNumber: 'TRK123' },
});
console.log('Created notification:', notification);Client-side Usage
import { NotificationClientSDK } from '@webdevarif/notifications/client';
// Create client
const client = new NotificationClientSDK({
baseUrl: 'http://localhost:3000/api',
websocketUrl: 'ws://localhost:3000',
autoConnect: true,
});
// Get notifications
const notifications = await client.get({
recipientId: 'user-123',
limit: 10,
});
// Mark as read
await client.markAsRead('notification-id');
// Listen for real-time updates
client.on('notification:created', (notification) => {
console.log('New notification:', notification);
});React Integration
import React from 'react';
import { useNotifications, NotificationClientSDK } from '@webdevarif/notifications/client';
const client = new NotificationClientSDK({
baseUrl: 'http://localhost:3000/api',
websocketUrl: 'ws://localhost:3000',
});
function NotificationList() {
const {
notifications,
count,
loading,
markAsRead,
markAll,
delete: deleteNotification,
} = useNotifications({
client,
filters: { recipientId: 'user-123' },
autoFetch: true,
});
if (loading) return <div>Loading...</div>;
return (
<div>
<h2>Notifications ({count?.unread || 0} unread)</h2>
<button onClick={() => markAll(true)}>
Mark All as Read
</button>
{notifications.map((notification) => (
<div key={notification.id} className={notification.read ? 'read' : 'unread'}>
<h3>{notification.title}</h3>
<p>{notification.body}</p>
<button onClick={() => markAsRead(notification.id)}>
Mark as Read
</button>
<button onClick={() => deleteNotification(notification.id)}>
Delete
</button>
</div>
))}
</div>
);
}🗄️ Storage Adapters
In-Memory Adapter (Default)
import { InMemoryStorageAdapter } from '@webdevarif/notifications';
const storageAdapter = new InMemoryStorageAdapter();Custom Database Adapter
import { BaseStorageAdapter } from '@webdevarif/notifications';
class MyDatabaseAdapter extends BaseStorageAdapter {
constructor(private db: any) {
super();
}
async save(notification) {
// Your database save logic
return this.db.notifications.create(notification);
}
async find(filters) {
// Your database query logic
return this.db.notifications.findMany(filters);
}
// ... implement other methods
}Prisma Adapter
import { PrismaStorageAdapter } from '@webdevarif/notifications';
import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();
const storageAdapter = new PrismaStorageAdapter(prisma);🔌 WebSocket Adapters
Default WebSocket Adapter
import { DefaultWebSocketAdapter } from '@webdevarif/notifications';
const wsAdapter = new DefaultWebSocketAdapter('ws://localhost:3000', {
reconnectInterval: 5000,
maxReconnectAttempts: 10,
});Socket.IO Adapter
import { SocketIOAdapter } from '@webdevarif/notifications';
import io from 'socket.io-client';
const socket = io('http://localhost:3000');
const wsAdapter = new SocketIOAdapter(socket);Pusher Adapter
import { PusherAdapter } from '@webdevarif/notifications';
import Pusher from 'pusher-js';
const pusher = new Pusher('your-app-key', {
cluster: 'your-cluster',
});
const wsAdapter = new PusherAdapter(pusher, 'notifications');🎨 Customization
Custom Notification Types
// Register custom notification types
notifications.registerType('order', {
icon: '📦',
color: '#f97316',
priority: 'normal',
actions: [
{ id: 'view', label: 'View Order', action: 'view_order' },
{ id: 'track', label: 'Track Package', action: 'track_package' },
],
});
notifications.registerType('payment', {
icon: '💳',
color: '#059669',
priority: 'high',
autoExpire: 24 * 60 * 60 * 1000, // 24 hours
});Lifecycle Hooks
const notifications = new Notifications({
storageAdapter,
hooks: {
onBeforeCreate: async (input) => {
// Modify notification before creation
return { ...input, meta: { ...input.meta, source: 'api' } };
},
onAfterCreate: async (notification) => {
// Send email, push notification, etc.
await sendEmail(notification.recipientId, notification);
},
onBeforeDelete: async (id) => {
// Check if deletion is allowed
return true;
},
},
});Event Listeners
// Listen to events
notifications.on('notification:created', (notification) => {
console.log('New notification created:', notification);
});
notifications.on('notification:updated', (notification) => {
console.log('Notification updated:', notification);
});
notifications.on('notification:deleted', (data) => {
console.log('Notification deleted:', data.id);
});🛠️ API Reference
Notifications Service
class Notifications {
// Create a notification
create(input: CreateNotificationInput): Promise<NotificationPayload>;
// Get notifications with filters
get(filters?: NotificationFilters): Promise<NotificationPayload[]>;
// Get single notification
getOne(id: string): Promise<NotificationPayload | null>;
// Mark as read/unread
markAsRead(id: string): Promise<boolean>;
markAsUnread(id: string): Promise<boolean>;
// Mark all notifications
markAll(recipientId: string, read: boolean): Promise<number>;
// Delete notification
delete(id: string): Promise<boolean>;
// Get count
count(filters?: NotificationFilters): Promise<NotificationCount>;
// Register custom type
registerType(type: string, config: NotificationTypeConfig): void;
}Client SDK
class NotificationClientSDK {
// Connect to WebSocket
connect(): Promise<void>;
disconnect(): void;
// API methods
get(filters?: NotificationFilters): Promise<NotificationPayload[]>;
getOne(id: string): Promise<NotificationPayload | null>;
markAsRead(id: string): Promise<boolean>;
markAsUnread(id: string): Promise<boolean>;
markAll(read: boolean): Promise<number>;
delete(id: string): Promise<boolean>;
count(filters?: NotificationFilters): Promise<NotificationCount>;
// Event listeners
on(event: string, callback: Function): void;
off(event: string, callback: Function): void;
}React Hooks
// Main hook
function useNotifications(options: UseNotificationsOptions): UseNotificationsReturn;
// Count only hook
function useNotificationCount(client: NotificationClientSDK, filters?: NotificationFilters);
// Real-time hook
function useRealtimeNotifications(client: NotificationClientSDK, onNotification?: Function);🚀 Express Server Example
See src/server/expressExample.ts for a complete Express.js server implementation with:
- REST API endpoints
- WebSocket support
- Custom notification types
- Lifecycle hooks
- Error handling
📝 TypeScript Support
The package is written in TypeScript and provides comprehensive type definitions:
interface NotificationPayload<T = any> {
id: string;
recipientId: string;
type: string;
title: string;
body: string;
data?: T;
meta?: Record<string, any>;
read: boolean;
createdAt: Date;
updatedAt: Date;
}
interface NotificationFilters {
recipientId?: string;
type?: string;
read?: boolean;
limit?: number;
offset?: number;
sortBy?: 'createdAt' | 'updatedAt';
sortOrder?: 'asc' | 'desc';
}🔧 Configuration
Server Configuration
interface NotificationConfig {
storageAdapter: StorageAdapter;
websocketAdapter?: WebSocketAdapter;
hooks?: NotificationHooks;
defaultType?: string;
maxRetries?: number;
retryDelay?: number;
}Client Configuration
interface ClientConfig {
baseUrl: string;
websocketUrl?: string;
apiKey?: string;
token?: string;
autoConnect?: boolean;
reconnectInterval?: number;
maxReconnectAttempts?: number;
}🧪 Testing
import { MockWebSocketAdapter } from '@webdevarif/notifications';
// Use mock adapters for testing
const mockWsAdapter = new MockWebSocketAdapter();
const notifications = new Notifications({
storageAdapter: new InMemoryStorageAdapter(),
websocketAdapter: mockWsAdapter,
});🤝 Contributing
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
📄 License
This project is licensed under the MIT License - see the LICENSE file for details.
🆘 Support
- 📧 Email: [email protected]
- 🐛 Issues: GitHub Issues
- 📚 Documentation: GitHub Wiki
🙏 Acknowledgments
- Built with ❤️ by the WebDevArif team
- Inspired by modern notification systems
- Thanks to all contributors and users
