# Migrating from Legacy to Novu Notifications This guide covers migrating from the old notification system to the new Novu-powered system. ## Migration Overview The new notification system: - Uses Novu for multi-channel delivery - Supports granular preferences per workflow - Includes frequency controls - Provides delivery tracking - Backwards compatible with existing preferences ## Data Migration ### Step 1: Migrate Existing User Preferences The system automatically migrates old preferences from `user_preferences.email_notifications` and `user_preferences.push_notifications` to the new `user_notification_preferences` table. Run this migration script to batch migrate existing users: ```sql -- Migrate existing users to new notification preferences INSERT INTO user_notification_preferences ( user_id, channel_preferences, workflow_preferences, frequency_settings ) SELECT user_id, jsonb_build_object( 'in_app', true, 'email', COALESCE((email_notifications->>'system_announcements')::boolean, true), 'push', COALESCE((push_notifications->>'browser_enabled')::boolean, false), 'sms', false ), jsonb_build_object( 'review-reply', COALESCE((email_notifications->>'review_replies')::boolean, true), 'new-follower', COALESCE((email_notifications->>'new_followers')::boolean, true), 'system-announcement', COALESCE((email_notifications->>'system_announcements')::boolean, true), 'weekly-digest', COALESCE((email_notifications->>'weekly_digest')::boolean, false), 'monthly-digest', COALESCE((email_notifications->>'monthly_digest')::boolean, true) ), jsonb_build_object( 'digest', 'daily', 'max_per_hour', 10 ) FROM user_preferences WHERE NOT EXISTS ( SELECT 1 FROM user_notification_preferences WHERE user_notification_preferences.user_id = user_preferences.user_id ); ``` ### Step 2: Create Novu Subscribers for Existing Users Create a batch script to register all existing users as Novu subscribers: ```typescript import { supabase } from '@/integrations/supabase/client'; import { notificationService } from '@/lib/notificationService'; async function migrateUsersToNovu() { const { data: profiles, error } = await supabase .from('profiles') .select('user_id, username, display_name') .is('banned', false); if (error) { console.error('Error fetching profiles:', error); return; } for (const profile of profiles) { // Get user's auth data for email const { data: { user } } = await supabase.auth.admin.getUserById(profile.user_id); if (user?.email) { await notificationService.createSubscriber({ subscriberId: profile.user_id, email: user.email, firstName: profile.display_name?.split(' ')[0], lastName: profile.display_name?.split(' ').slice(1).join(' '), data: { username: profile.username, }, }); console.log(`Migrated user: ${profile.username}`); // Rate limit to avoid overwhelming Novu API await new Promise(resolve => setTimeout(resolve, 100)); } } console.log('Migration complete!'); } ``` ## Code Migration ### Updating Notification Triggers **Old Way:** ```typescript // Direct database insert (legacy) await supabase .from('notifications') .insert({ user_id: userId, type: 'review_reply', content: 'Someone replied to your review', }); ``` **New Way:** ```typescript // Using Novu via notificationService import { notificationService } from '@/lib/notificationService'; await notificationService.trigger({ workflowId: 'review-reply', subscriberId: userId, payload: { reviewTitle: 'Great ride!', replyAuthor: 'Jane Doe', replyContent: 'Thanks for the review!', reviewUrl: `/reviews/${reviewId}`, }, }); ``` ### Updating Preference Management **Old Way:** ```typescript await supabase .from('user_preferences') .update({ email_notifications: { review_replies: false, }, }) .eq('user_id', userId); ``` **New Way:** ```typescript import { notificationService } from '@/lib/notificationService'; await notificationService.updatePreferences(userId, { channelPreferences: { in_app: true, email: true, push: false, sms: false, }, workflowPreferences: { 'review-reply': false, }, frequencySettings: { digest: 'daily', max_per_hour: 10, }, }); ``` ## Rollback Plan If you need to rollback to the old system: 1. **Keep Old Tables**: Don't drop `user_preferences.email_notifications` or `push_notifications` columns 2. **Disable Novu**: Clear `VITE_NOVU_APPLICATION_IDENTIFIER` environment variable 3. **Revert Code**: The notification service gracefully handles missing Novu config ## Testing the Migration ### Test Checklist - [ ] All existing users have entries in `user_notification_preferences` - [ ] User preferences match their previous settings - [ ] Test users receive notifications on all enabled channels - [ ] Webhook is receiving delivery events - [ ] Notification logs are being created - [ ] User preference changes sync to Novu - [ ] Unsubscribe links work correctly - [ ] Email templates render correctly - [ ] Push notifications work in supported browsers - [ ] In-app notifications display correctly ### Test Script ```typescript // Test notification delivery import { notificationService } from '@/lib/notificationService'; async function testNotifications(testUserId: string) { console.log('Testing notification delivery...'); // Test each workflow const workflows = [ { id: 'review-reply', payload: { reviewTitle: 'Test Review', replyAuthor: 'Test User', replyContent: 'Test reply content', reviewUrl: '/test', }, }, { id: 'new-follower', payload: { followerName: 'Test Follower', followerProfile: '/profile/test', }, }, ]; for (const workflow of workflows) { const result = await notificationService.trigger({ workflowId: workflow.id, subscriberId: testUserId, payload: workflow.payload, }); console.log(`${workflow.id}: ${result.success ? '✓' : '✗'}`); if (!result.success) { console.error(`Error: ${result.error}`); } } } ``` ## Performance Considerations ### Batch Operations When migrating large numbers of users: 1. **Rate Limiting**: Add delays between API calls 2. **Batch Size**: Process in chunks of 100-500 users 3. **Error Handling**: Log failures and retry 4. **Progress Tracking**: Store migration state ### Optimization Tips 1. **Cache Templates**: Load notification templates once at startup 2. **Async Processing**: Use edge functions for heavy operations 3. **Database Indexes**: Already created on `user_notification_preferences` 4. **Connection Pooling**: Supabase handles this automatically ## Monitoring Post-Migration Track these metrics after migration: ```sql -- Delivery success rate SELECT DATE(created_at) as date, COUNT(*) FILTER (WHERE status = 'delivered') * 100.0 / COUNT(*) as success_rate FROM notification_logs WHERE created_at > NOW() - INTERVAL '7 days' GROUP BY DATE(created_at) ORDER BY date DESC; -- Channel usage SELECT channel, COUNT(*) as total_sent, COUNT(*) FILTER (WHERE status = 'delivered') as delivered, COUNT(*) FILTER (WHERE read_at IS NOT NULL) as read FROM notification_logs WHERE created_at > NOW() - INTERVAL '7 days' GROUP BY channel; -- User engagement SELECT COUNT(DISTINCT user_id) as active_users, AVG(EXTRACT(EPOCH FROM (read_at - delivered_at))) as avg_time_to_read_seconds FROM notification_logs WHERE read_at IS NOT NULL AND created_at > NOW() - INTERVAL '7 days'; ``` ## Support During Migration 1. **Backup Data**: Export old notification preferences before migration 2. **Phased Rollout**: Enable for a subset of users first 3. **Feature Flag**: Use admin settings to control Novu enablement 4. **User Communication**: Notify users of new notification features 5. **Monitor Logs**: Watch edge function logs during migration ## Timeline Recommended migration timeline: - **Week 1**: Test with internal users - **Week 2**: Rollout to 10% of users - **Week 3**: Rollout to 50% of users - **Week 4**: Complete rollout - **Week 5**: Remove legacy code (optional)