Files
Kordant/tasks/kordant-unified-restructure/14-notifications-router.md
2026-05-25 22:49:37 -04:00

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 unified notifications router.

deliverables:

  • web/src/server/api/routers/notification.ts — Notification router:
    • notification.sendEmailadminProcedure (or internal service use) sending transactional email
    • notification.sendPushprotectedProcedure sending push to user's devices
    • notification.sendSMSprotectedProcedure sending SMS
    • notification.registerDeviceprotectedProcedure registering FCM/APNs token
    • notification.unregisterDeviceprotectedProcedure removing device token
    • notification.listDevicesprotectedProcedure listing registered devices
    • notification.getPreferencesprotectedProcedure getting notification preferences
    • notification.updatePreferencesprotectedProcedure updating preferences
  • web/src/server/services/notification.service.ts — Business logic:
    • sendEmail(to, subject, html, text?) — via Resend API
    • sendPush(deviceTokens, title, body, data?) — via FCM for Android, APNs for iOS
    • sendSMS(phoneNumber, message) — via Twilio
    • registerDevice(userId, token, platform, deviceType) — save to DB
    • unregisterDevice(userId, token) — soft-delete or mark inactive
    • getPreferences(userId) — read from DB
    • updatePreferences(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 client
    • web/src/server/lib/fcm.ts — Firebase Admin SDK initialization
    • web/src/server/lib/twilio.ts — Twilio client

steps:

  1. Install dependencies in web/:
    • resend (email)
    • firebase-admin (push notifications)
    • twilio (SMS)
  2. Create provider client files:
    • resend.ts: initialize with RESEND_API_KEY
    • fcm.ts: initialize Firebase Admin with service account JSON
    • twilio.ts: initialize with TWILIO_ACCOUNT_SID and TWILIO_AUTH_TOKEN
  3. 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)
  4. Create web/src/server/services/notification.service.ts:
    • sendEmail: call Resend, log result, handle errors
    • sendPush: 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 format
    • registerDevice: upsert into DeviceToken table
    • unregisterDevice: set isActive = false
    • getPreferences / updatePreferences: read/write a NotificationPreferences table (add to schema if missing)
  5. Create web/src/server/api/routers/notification.ts:
    • Define Zod schemas for all inputs
    • sendEmail: restricted to admin or internal service calls
    • sendPush: accepts title, body, data payload; sends to all active user devices
    • sendSMS: accepts phoneNumber, message
    • Device registration/unregistration procedures
    • Preference get/update procedures
  6. Wire router into web/src/server/api/root.ts.
  7. Write unit tests with mocked providers.

steps:

  • Unit: sendEmail calls Resend with correct parameters
  • Unit: sendPush iterates tokens and calls FCM
  • Unit: sendSMS validates E.164 format before calling Twilio
  • Unit: registerDevice creates DeviceToken record
  • Unit: unregisterDevice marks token inactive
  • Integration: notification.registerDevice persists 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 test for 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_PATH env var.
  • For APNs, Firebase Admin can proxy to APNs if configured correctly. Alternatively, use apn npm 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.queue table for reliable delivery (retry failed sends). This can be a follow-up task.
  • The NotificationPreferences model may need to be added to the Drizzle schema if not already present. Add it as part of this task.