- Add self-hosted push notifications using VAPID (no external services) - Create PushSubscription model in database - Implement server-side push logic with web-push library - Add notification helpers for list events (items, invitations, collaborators) - Create NotificationSettings UI component for profile page - Add usePushNotifications React hook for subscription management - Create API routes for subscription management and VAPID key - Update service worker with push event handlers - Integrate notifications into item actions (add, remove, complete) - Integrate notifications into sharing actions (invite, accept, remove) - Add VAPID key generation script - Add comprehensive documentation and setup guides Notifications are sent when: - Items are added/removed/completed in shared lists - Users are invited to lists - Invitations are accepted - Collaborators are removed from lists Works on iOS 16.4+ (when installed to home screen), Android, and desktop browsers.
7.4 KiB
Web Push Notifications - Complete Implementation ✅
What Was Implemented
A complete, self-hosted Web Push notification system for your PWA shopping list app.
📦 Files Created
Core Implementation
lib/push-notifications.ts- Server-side push notification logic with VAPIDlib/notification-helpers.ts- Helper functions for specific notification eventshooks/usePushNotifications.ts- React hook for managing subscriptionscomponents/NotificationSettings.tsx- UI component for enabling/disabling notifications
API Routes
app/api/push/vapid-key/route.ts- Returns public VAPID keyapp/api/push/subscribe/route.ts- Saves push subscriptionsapp/api/push/unsubscribe/route.ts- Removes push subscriptions
Database
- Updated
prisma/schema.prisma- AddedPushSubscriptionmodel
Service Worker
- Updated
public/sw.js- Added push event handlers
Scripts & Config
scripts/generate-vapid-keys.js- Generate VAPID keys- Updated
package.json- Added web-push dependency and npm script - Updated
.env.example- Added VAPID key placeholders
Documentation
PUSH_NOTIFICATIONS_QUICK_START.md- 5-minute setup guidePUSH_NOTIFICATIONS_SETUP.md- Complete setup and configurationPUSH_NOTIFICATIONS_INTEGRATION_EXAMPLE.md- Code integration examplesPUSH_NOTIFICATIONS_GUIDE.md- Architecture and testing guidePUSH_NOTIFICATIONS_COMPLETE.md- This file
🎯 How It Works
The Push Notification Server
You don't need an external service! This implementation uses VAPID (Voluntary Application Server Identification), which is a standard protocol that allows your own server to send push notifications directly to browsers.
Here's the flow:
-
Generate VAPID Keys (one-time setup)
- Run
npm run vapid:generate - These are like JWT secrets - a public/private key pair
- Public key: shared with browsers
- Private key: kept secret on your server
- Run
-
User Subscribes
- User clicks "Enable" in NotificationSettings
- Browser creates a unique push subscription (includes endpoint URL)
- Subscription saved to your database
-
Event Occurs
- Someone adds an item to a shared list
- Your server action calls
notifyItemAdded() - Function looks up all subscriptions for list members
- Sends push notification using
web-pushlibrary
-
Browser Receives
- Browser's push service receives the notification
- Service worker wakes up and displays it
- User sees notification even if app is closed
The "Server" is Just Your App
The push notification "server" is simply:
- The
web-pushnpm package (installed in your app) - Your VAPID keys (in your .env file)
- Your existing Next.js API routes
No external service, no additional infrastructure needed!
🚀 Setup Instructions
Step 1: Install Dependencies
npm install
Step 2: Generate VAPID Keys
npm run vapid:generate
Copy the output to your .env file:
VAPID_PUBLIC_KEY=BNxxx...
VAPID_PRIVATE_KEY=xxx...
VAPID_SUBJECT=mailto:your-email@example.com
Step 3: Update Database
npx prisma db push
Step 4: Add UI Component
In app/profile/page.tsx:
import NotificationSettings from '@/components/NotificationSettings';
export default function ProfilePage() {
return (
<div>
{/* Your existing content */}
<NotificationSettings />
</div>
);
}
Step 5: Integrate Notifications
In your action files (see PUSH_NOTIFICATIONS_INTEGRATION_EXAMPLE.md for details):
import { notifyItemAdded } from '@/lib/notification-helpers';
// After creating an item:
await notifyItemAdded(listId, item.title, session.user.id);
📱 Notification Events
| Event | Function | When |
|---|---|---|
| Item added | notifyItemAdded() |
Someone adds item to shared list |
| Item removed | notifyItemRemoved() |
Someone removes item |
| Item completed | notifyItemCompleted() |
Someone marks item complete |
| List invitation | notifyListInvitation() |
Someone invites you |
| Invitation accepted | notifyInvitationAccepted() |
Someone accepts your invite |
| Removed from list | notifyRemovedFromList() |
Owner removes you |
🌐 Browser Support
| Platform | Support | Notes |
|---|---|---|
| Chrome Desktop | ✅ Full | Works perfectly |
| Chrome Android | ✅ Full | Works perfectly |
| Firefox Desktop | ✅ Full | Works perfectly |
| Firefox Android | ✅ Full | Works perfectly |
| Safari macOS | ✅ 16.4+ | Works when installed |
| Safari iOS | ✅ 16.4+ | Must be installed to home screen |
| Edge | ✅ Full | Chromium-based |
iOS Requirements
- iOS 16.4 or later
- Must use "Add to Home Screen" (won't work in Safari browser)
- Requires HTTPS in production
🔒 Security
- VAPID private key must be kept secret (like JWT secret)
- Subscriptions are tied to authenticated users
- Expired subscriptions automatically cleaned up
- No cross-user notification access
🧪 Testing
Desktop (Chrome)
- Start dev server:
npm run dev - Open DevTools → Application → Service Workers
- Enable notifications in profile
- Trigger an action (add item, etc.)
- See notification appear
iOS (Real Device)
- Deploy with HTTPS
- Open in Safari
- Share → Add to Home Screen
- Open installed app
- Enable notifications
- Test from another device
📊 Database Schema
New table added:
model PushSubscription {
id String @id @default(cuid())
userId String
endpoint String @db.Text
p256dh String @db.Text
auth String @db.Text
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
user User @relation(...)
}
🎨 Customization
Change Notification Content
Edit lib/notification-helpers.ts to customize titles, bodies, icons.
Add Per-List Preferences
- Add
notificationsEnabledtoCollaboratormodel - Check this field before sending notifications
- Add UI toggle in list settings
Add Notification Sounds
Update service worker push event:
const options = {
...existing,
silent: false,
sound: '/notification-sound.mp3',
};
🐛 Troubleshooting
"Push notifications not configured"
- Verify VAPID keys are in
.env - Restart dev server
Notifications not appearing
- Check browser permission (must be "granted")
- Verify service worker is active
- Check console for errors
- Ensure HTTPS (or localhost)
iOS not working
- Check iOS version (16.4+)
- Must be installed to home screen
- Check Settings → [App] → Notifications
📈 Performance
- Notifications sent asynchronously (non-blocking)
- Multiple devices per user supported
- Automatic cleanup of expired subscriptions
- TTL: 24 hours
✅ Production Checklist
- Generate production VAPID keys
- Add to production environment variables
- Run
npx prisma db pushon production DB - Verify HTTPS is working
- Test on actual iOS device
- Monitor logs for errors
🎉 You're Done!
Your app now has:
- ✅ Self-hosted push notifications
- ✅ No external dependencies
- ✅ iOS and Android support
- ✅ Multi-device support
- ✅ Automatic subscription management
Users will receive notifications when:
- Items are added/removed/completed in shared lists
- They're invited to lists
- Their invitations are accepted
- They're removed from lists
All without any external services or additional infrastructure!