Push Notifications
Push Notifications Package (@repo/push-notifications
)
A comprehensive push notification service supporting multiple providers (Expo, Firebase, Web Push) for sending and managing push notifications with user activity tracking.
Why You Need This
Engaging users across devices requires reliable push notifications. This package unifies provider APIs so you can deliver messages without writing custom code for each service. Check the Getting Started guide to enable provider credentials.
Features
- Multi-provider support (Expo, Firebase Cloud Messaging, Web Push)
- Database integration (
@repo/database
) for notification tracking and user preferences - User activity tracking via
lastActiveAt
timestamps - Support for identifying inactive users
- Pre-built notification templates (
src/templates
) - Support for device token management (registration, removal)
- Message status tracking (pending, sent, delivered, read)
- Respects user preferences and "Do Not Disturb" settings
- TypeScript support
Database Schema
This package relies on schemas defined in @repo/database/src/schema/notifications.ts
, which includes tables like:
pushNotifications
: Stores individual sent notifications, their content, status, and associated user.notificationPreferences
: Stores user-level preferences for receiving different types of notifications.deviceTokens
: Stores registered device tokens (from Expo, FCM, Web Push) linked to users, along with device metadata and last activity timestamp.
Setup
- Install Dependencies: Ensure necessary provider SDKs are installed (e.g.,
expo-server-sdk-node
,firebase-admin
,web-push
). - Configure Provider: Set up environment variables for your chosen provider(s).
Expo Setup
EXPO_ACCESS_TOKEN=your_expo_access_token
Firebase Setup
# Path to your Firebase service account JSON file
FIREBASE_SERVICE_ACCOUNT_PATH=path/to/service-account.json
# Your Firebase project's Realtime Database URL (often optional)
FIREBASE_DATABASE_URL=https://your-project.firebaseio.com
Web Push Setup
# VAPID Public Key
WEB_PUSH_PUBLIC_KEY=your_vapid_public_key
# VAPID Private Key
WEB_PUSH_PRIVATE_KEY=your_vapid_private_key
# Contact email for VAPID
WEB_PUSH_SUBJECT=mailto:your-email@example.com
Info: You can generate VAPID keys using the
WebPushProvider.generateVAPIDKeys()
static method from this package. Remember to store these keys securely.
Core Service: PushNotificationService
The main entry point is the PushNotificationService
class.
Initialization
Create an instance, specifying the provider and its configuration.
import { PushNotificationService } from '@repo/push-notifications';
// Example: Initialize for Expo
const pushService = PushNotificationService.create({
provider: 'expo',
expo: {
accessToken: process.env.EXPO_ACCESS_TOKEN,
},
// Optionally configure other providers if needed
// firebase: { ... },
// web: { ... },
});
Sending Notifications
// Send a notification to a specific user ID
// The service looks up the user's registered & active device tokens
await pushService.sendToUser({
userId: 'user123',
title: 'New Message',
body: 'You have received a new message.',
data: { // Optional data payload
messageId: 'msg_abc',
actionUrl: '/messages/msg_abc'
}
});
Using Templates
The package provides pre-built notification templates (e.g., newLoginNotification
) which return structured title, body, and data.
import { PushNotificationService, newLoginNotification } from '@repo/push-notifications';
const pushService = PushNotificationService.create({ /* ... */ });
const deviceInfo = { deviceType: 'Mobile', os: 'Android' };
const location = { city: 'London', country: 'UK', ip: '...' };
// Generate notification content from the template
const notificationContent = newLoginNotification(deviceInfo, location);
await pushService.sendToUser({
userId: 'user456',
title: notificationContent.title,
body: notificationContent.body,
data: notificationContent.data,
});
Available Templates (check src/templates/index.ts
):
newLoginNotification
systemUpdateNotification
messageReceivedNotification
Managing Notifications
// Get notifications for a user (with pagination and filtering)
const userNotifications = await pushService.getUserNotifications('user123', {
limit: 10,
offset: 0,
status: 'delivered' // Filter by status (optional)
});
// Mark a specific notification as read by the user
// This also updates the user's last activity timestamp
await pushService.markAsRead('notification_xyz', 'user123');
Device Token Management
Register and remove device tokens as users log in/out or grant/revoke permissions.
import { registerDeviceToken, removeDeviceToken } from '@repo/push-notifications';
// Register a token (e.g., on login or app startup)
await registerDeviceToken(
'user123', // The user ID
'ExponentPushToken[xxxxxxxxxxxxxx]', // The actual token
'expo', // The provider ('expo', 'fcm', 'web')
{
// Optional device metadata
deviceName: 'Pixel 6',
osName: 'Android',
osVersion: '13'
}
);
// Remove a token (e.g., on logout)
await removeDeviceToken('ExponentPushToken[xxxxxxxxxxxxxx]');
User Activity Tracking
Keep track of when users were last active to identify inactive users or potentially suppress notifications.
import { updateUserActivity, updateTokenActivity } from '@repo/push-notifications';
// Update activity for a specific token (e.g., app opened)
await updateTokenActivity('ExponentPushToken[yyyyyyyyyyyyyy]');
// Update activity for ALL of a user's tokens (e.g., user performed an action)
await updateUserActivity('user123');
// Example usage in an API route (update activity in the background)
app.get('/api/some-action', async (c) => {
const user = c.get('user'); // Assuming user is available in context
if (user) {
updateUserActivity(user.id).catch(console.error);
}
// ... rest of the route handler ...
return c.json({ success: true });
});
// Find users inactive since a specific date
const cutoffDate = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000); // 30 days ago
const inactiveUsers = await pushService.getInactiveUsers(cutoffDate);
// Returns array of user IDs and their last active timestamp
Warning: Regularly calling
updateUserActivity
orupdateTokenActivity
is crucial for the accuracy of inactive user detection.
Providers
- Expo: Uses
expo-server-sdk-node
. - Firebase (FCM): Uses
firebase-admin
. - Web Push: Uses
web-push
library and requires client-side Service Worker implementation for subscription management.