Push notification module for Medusa.js using Firebase Cloud Messaging
npm install @zan-shop/push-notificationsPush notification module for Medusa.js using Firebase Cloud Messaging (FCM). Provides type-safe utilities for sending mobile push notifications with built-in formatters for common e-commerce events.
- ๐ฅ Firebase Cloud Messaging integration
- ๐ฑ iOS & Android support
- ๐ฆ Pre-built formatters (orders, shipments, returns, payouts)
- ๐ Batch sending (up to 500 devices)
- โ
Full TypeScript support
- ๐งช Dry-run mode for testing
``bash`
npm install @zan-shop/push-notifications
Prerequisites:
- Firebase project with Cloud Messaging enabled
- Firebase Admin SDK already initialized in your project
- Node.js >= 18.0.0
`typescript
import { FCMService } from '@zan-shop/push-notifications';
const fcmService = new FCMService({
firebaseApp,
dryRun: false, // Set to true for testing
});
`
`typescript
import { formatShipmentNotification } from '@zan-shop/push-notifications';
const notification = formatShipmentNotification({
type: 'shipment.created',
orderId: 'order_123',
trackingNumber: 'TRACK123',
carrier: 'FedEx',
estimatedDelivery: '2024-01-15',
});
const result = await fcmService.sendToDevice(deviceToken, notification);
if (result.success) {
console.log('Notification sent:', result.messageId);
} else {
console.error('Failed to send:', result.error);
}
`
Pre-built formatters for common e-commerce events:
`typescript
import { formatShipmentNotification } from '@zan-shop/push-notifications';
const notification = formatShipmentNotification({
type: 'shipment.created',
orderId: 'order_123',
trackingNumber: 'TRACK123',
carrier: 'FedEx',
});
// Output: "๐ฆ Order Shipped - Your order #order_123 has been shipped! Track your package..."
`
`typescript
import { formatOrderNotification } from '@zan-shop/push-notifications';
const notification = formatOrderNotification({
type: 'order.placed',
orderId: 'order_123',
orderNumber: 'ORD-2024-001',
});
// Output: "โ
Order Confirmed - Your order ORD-2024-001 has been confirmed!"
`
`typescript
import { formatReturnNotification } from '@zan-shop/push-notifications';
const notification = formatReturnNotification({
type: 'return.approved',
returnId: 'return_123',
orderId: 'order_123',
});
// Output: "โ
Return Approved - Your return request for order #order_123 has been approved!"
`
`typescript
import type { NotificationPayload } from '@zan-shop/push-notifications';
const customNotification: NotificationPayload = {
title: '๐ Special Offer',
body: 'Get 20% off your next purchase!',
data: {
type: 'promotion',
promoCode: 'SAVE20',
},
imageUrl: 'https://example.com/promo-banner.jpg',
clickAction: '/promotions/save20',
priority: 'high',
sound: 'default',
badge: 1,
};
await fcmService.sendToDevice(deviceToken, customNotification);
`
`typescript
const tokens = ['token1', 'token2', 'token3'];
const result = await fcmService.sendToMultipleDevices(tokens, notification);
console.log(Success: ${result.successCount}, Failed: ${result.failureCount});`
Example subscriber for shipment notifications:
`typescript
import type { SubscriberArgs } from '@medusajs/framework';
import { FCMService, formatShipmentNotification } from '@zan-shop/push-notifications';
export default async function handleShipmentCreated({
event,
container,
}: SubscriberArgs<{ id: string }>) {
const fcmService = container.resolve
const deviceService = container.resolve('deviceService');
const shipment = await fetchShipmentDetails(event.data.id);
// Get customer's devices
const devices = await deviceService.getCustomerDevices(shipment.order.customer_id);
if (devices.length === 0) return;
// Format notification
const notification = formatShipmentNotification({
type: 'shipment.created',
orderId: shipment.order.id,
trackingNumber: shipment.tracking_number,
carrier: shipment.carrier,
});
// Send to all customer devices
const tokens = devices.map(d => d.token);
const result = await fcmService.sendToMultipleDevices(tokens, notification);
console.log(Sent: ${result.successCount} succeeded, ${result.failureCount} failed);`
}
- sendToDevice(token, payload) - Send to single devicesendToMultipleDevices(tokens, payload)
- - Batch send (auto-splits into batches of 500)sendToCustomer(customerId, payload)
- - Send to all customer devicessendToTopic(topic, payload)
- - Broadcast to topic subscribersvalidateToken(token)
- - Check if token is valid
- formatShipmentNotification(data) - Shipment eventsformatOrderNotification(data)
- - Order eventsformatReturnNotification(data)
- - Return eventsformatPayoutNotification(data)
- - Payout events
`typescript`
import type {
NotificationPayload,
SendResult,
BatchResult,
DeviceToken,
DeviceRegistration,
} from '@zan-shop/push-notifications';
The package provides an abstract DeviceService class. Extend it with your database implementation:
`typescript
import { DeviceService } from '@zan-shop/push-notifications';
class MyDeviceService extends DeviceService {
async registerDevice(customerId, deviceData) { / ... / }
async getCustomerDevices(customerId) { / ... / }
async deactivateDevice(token) { / ... / }
// ... other methods
}
`
Enable dry-run mode for testing without sending actual notifications:
`typescript`
const fcmService = new FCMService({ firebaseApp, dryRun: true });
This package uses semantic-release for automated versioning. Use conventional commits:
`For features (bumps minor version 1.0.0 -> 1.1.0)
git commit -m "feat: add notification scheduling"
BREAKING CHANGE: sendToDevice now requires configuration object"
MIT
- GitHub Repository
- Issues
- Firebase Cloud Messaging Docs
- Medusa.js Docs