5.6 KiB
5.6 KiB
14. Backend Router — Email, Push, and SMS Notifications
meta: id: kordant-unified-restructure-14 feature: kordant-unified-restructure priority: P1 depends_on: [kordant-unified-restructure-11] tags: [backend, notifications, email, push, sms, api]
objective:
- Build the tRPC router and service layer for sending notifications across all channels: email (Resend), push (FCM/APNs), and SMS (Twilio). Port logic from
packages/shared-notifications/into a unifiednotificationsrouter.
deliverables:
web/src/server/api/routers/notification.ts— Notification router:notification.sendEmail—adminProcedure(or internal service use) sending transactional emailnotification.sendPush—protectedProceduresending push to user's devicesnotification.sendSMS—protectedProceduresending SMSnotification.registerDevice—protectedProcedureregistering FCM/APNs tokennotification.unregisterDevice—protectedProcedureremoving device tokennotification.listDevices—protectedProcedurelisting registered devicesnotification.getPreferences—protectedProceduregetting notification preferencesnotification.updatePreferences—protectedProcedureupdating preferences
web/src/server/services/notification.service.ts— Business logic:sendEmail(to, subject, html, text?)— via Resend APIsendPush(deviceTokens, title, body, data?)— via FCM for Android, APNs for iOSsendSMS(phoneNumber, message)— via TwilioregisterDevice(userId, token, platform, deviceType)— save to DBunregisterDevice(userId, token)— soft-delete or mark inactivegetPreferences(userId)— read from DBupdatePreferences(userId, prefs)— write to DB
web/src/server/services/email.templates.ts— Email template renderer:- Welcome email
- Alert notification email
- Password reset email
- Family invite email
- Billing receipt email
- Provider clients:
web/src/server/lib/resend.ts— Resend clientweb/src/server/lib/fcm.ts— Firebase Admin SDK initializationweb/src/server/lib/twilio.ts— Twilio client
steps:
- Install dependencies in
web/:resend(email)firebase-admin(push notifications)twilio(SMS)
- Create provider client files:
resend.ts: initialize withRESEND_API_KEYfcm.ts: initialize Firebase Admin with service account JSONtwilio.ts: initialize withTWILIO_ACCOUNT_SIDandTWILIO_AUTH_TOKEN
- Create
web/src/server/services/email.templates.ts:- Each template is a function returning
{ subject, html, text } - Use simple HTML with inline CSS (or a minimal template engine)
- Include Kordant branding (logo, colors)
- Each template is a function returning
- Create
web/src/server/services/notification.service.ts:sendEmail: call Resend, log result, handle errorssendPush: iterate device tokens, call FCM for Android tokens, APNs for iOS tokens (or use Firebase Admin which handles both)sendSMS: call Twilio Messages API, validate E.164 phone formatregisterDevice: upsert intoDeviceTokentableunregisterDevice: setisActive = falsegetPreferences/updatePreferences: read/write aNotificationPreferencestable (add to schema if missing)
- Create
web/src/server/api/routers/notification.ts:- Define Zod schemas for all inputs
sendEmail: restricted to admin or internal service callssendPush: acceptstitle,body,datapayload; sends to all active user devicessendSMS: acceptsphoneNumber,message- Device registration/unregistration procedures
- Preference get/update procedures
- Wire router into
web/src/server/api/root.ts. - Write unit tests with mocked providers.
steps:
- Unit:
sendEmailcalls Resend with correct parameters - Unit:
sendPushiterates tokens and calls FCM - Unit:
sendSMSvalidates E.164 format before calling Twilio - Unit:
registerDevicecreates DeviceToken record - Unit:
unregisterDevicemarks token inactive - Integration:
notification.registerDevicepersists token to DB
acceptance_criteria:
- Email can be sent via Resend with branded templates
- Push notifications can be sent to registered Android and iOS devices via FCM
- SMS can be sent via Twilio to valid E.164 numbers
- Device tokens can be registered and unregistered per user
- Notification preferences are persisted and respected (e.g., user can disable SMS alerts)
- All provider errors are caught and logged without crashing the app
- Email templates include Kordant branding
validation:
- Send a test email via Resend to your own address and verify receipt
- Register a test device token and send a push (use Firebase Console or API)
- Send a test SMS via Twilio (use test credentials for free)
- Run
cd web && pnpm testfor notification service tests
notes:
- Reference legacy:
packages/shared-notifications/src/,packages/api/src/routes/notifications.routes.ts - For FCM, Firebase Admin SDK requires a service account JSON. Store path in
FIREBASE_SERVICE_ACCOUNT_PATHenv var. - For APNs, Firebase Admin can proxy to APNs if configured correctly. Alternatively, use
apnnpm package for direct APNs integration. - Twilio test credentials (
TWILIO_TEST_*) can be used for integration tests without sending real SMS. - Resend has a generous free tier (100 emails/day) perfect for development.
- Consider adding a
notification.queuetable for reliable delivery (retry failed sends). This can be a follow-up task. - The
NotificationPreferencesmodel may need to be added to the Drizzle schema if not already present. Add it as part of this task.