Skip to main content

FCM Push Notifications - Quick Start Guide

What Was Done

We’ve implemented Firebase Cloud Messaging (FCM) to enable native push notifications for your mobile app. This works alongside your existing in-app notification system.

Files Created/Modified

New Files

  1. Type Definitions
    • Updated types/models/users.ts - Added FCMToken type and fcmTokens field to user model
  2. API Endpoints
    • pages/api/notifications/register-fcm-token.ts - Register device tokens
    • pages/api/notifications/remove-fcm-token.ts - Remove device tokens
    • pages/api/notifications/update-fcm-token-usage.ts - Update token usage
  3. Backend Utilities
    • firebase/server/fcm/buildNotificationPayload.ts - Converts notifications to FCM format
    • firebase/server/fcm/sendPushNotification.ts - Sends FCM push notifications
    • firebase/server/fcm/index.ts - FCM utilities export
  4. Frontend Hooks
    • hooks/notifications/useFCMTokenRegistration.ts - React hooks for token management
  5. Documentation
    • docs/engineering/notifications/fcm-push-notifications.mdx - Complete implementation guide
    • docs/engineering/notifications/fcm-quick-start.md - This file

Modified Files

  • firebase/server/notifications.ts - Extended to send FCM push notifications

How It Works

1. User opens mobile app
2. App requests notification permissions
3. Device provides FCM token
4. App registers token with backend (POST /api/notifications/register-fcm-token)
5. Token stored in user document (users/{userId}.fcmTokens)
6. When event occurs (e.g., new booking):
   - createNotification() saves to Firestore
   - Automatically sends FCM push to all user's devices
7. Device receives notification, user taps
8. App opens to relevant screen

Next Steps for Mobile App

1. Install Dependencies (React Native/Expo)

# Expo
npx expo install expo-notifications expo-device expo-constants

# Or React Native Firebase
npm install @react-native-firebase/app @react-native-firebase/messaging

2. Request Permissions & Get Token

import * as Notifications from "expo-notifications";
import * as Device from "expo-device";
import { Platform } from "react-native";
import { useRegisterFCMToken } from "@/hooks/notifications/useFCMTokenRegistration";

async function setupPushNotifications() {
  // Check if physical device
  if (!Device.isDevice) {
    alert("Push notifications only work on physical devices");
    return null;
  }

  // Request permissions
  const { status } = await Notifications.requestPermissionsAsync();
  if (status !== "granted") {
    alert("Push notification permissions are required");
    return null;
  }

  // Get FCM token
  const token = (await Notifications.getExpoPushTokenAsync()).data;

  return token;
}

3. Register Token with Backend

// In your App.tsx or root component
import { useRegisterFCMToken } from '@/hooks/notifications/useFCMTokenRegistration';
import { useAuth } from '@/lib/AuthContext';

function App() {
  const { user } = useAuth();
  const { mutate: registerToken } = useRegisterFCMToken();

  useEffect(() => {
    if (user) {
      setupPushNotifications().then(token => {
        if (token) {
          registerToken({
            token,
            deviceId: Device.deviceId || 'unknown',
            platform: Platform.OS as 'ios' | 'android',
          });
        }
      });
    }
  }, [user]);

  return <YourApp />;
}

4. Handle Notification Taps

import { useEffect } from "react";
import * as Notifications from "expo-notifications";
import { useNavigation } from "@react-navigation/native";

function NotificationHandler() {
  const navigation = useNavigation();

  useEffect(() => {
    // Handle notification taps
    const subscription = Notifications.addNotificationResponseReceivedListener(
      (response) => {
        const data = response.notification.request.content.data;

        // Navigate based on notification type
        if (data.screen === "booking" && data.bookingId) {
          navigation.navigate("BookingDetail", {
            id: data.bookingId,
          });
        }
      },
    );

    return () => subscription.remove();
  }, [navigation]);

  return null;
}

5. Configure Notification Behavior

// Configure how notifications are shown
Notifications.setNotificationHandler({
  handleNotification: async () => ({
    shouldShowAlert: true,
    shouldPlaySound: true,
    shouldSetBadge: true,
  }),
});

// For Android, set up notification channel
if (Platform.OS === "android") {
  Notifications.setNotificationChannelAsync("default", {
    name: "Default",
    importance: Notifications.AndroidImportance.MAX,
    vibrationPattern: [0, 250, 250, 250],
    lightColor: "#FF231F7C",
  });
}

Testing

1. Test Token Registration

// Add logging to verify
registerToken(
  {
    token,
    deviceId,
    platform,
  },
  {
    onSuccess: () => console.log("✅ Token registered"),
    onError: (error) => console.error("❌ Registration failed:", error),
  },
);

2. Trigger a Test Notification

  • Create a test booking in your app
  • The notification should automatically be sent
  • Check device for push notification

3. Test from Firebase Console

  1. Go to Firebase Console → Cloud Messaging
  2. Click “Send test message”
  3. Enter your device’s FCM token
  4. Send notification

Platform-Specific Setup

iOS

  1. Enable Push Notifications in Xcode
    • Open iOS project in Xcode
    • Select target → Signing & Capabilities
    • Add “Push Notifications” capability
  2. Configure APNs
    • Create APNs key in Apple Developer Console
    • Upload to Firebase Console (Project Settings → Cloud Messaging → iOS)
  3. Update Info.plist (if needed)
    <key>UIBackgroundModes</key>
    <array>
      <string>remote-notification</string>
    </array>
    

Android

  1. Add google-services.json
    • Download from Firebase Console
    • Place in android/app/ directory
  2. Update AndroidManifest.xml (usually automatic with Expo)
    <uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
    

Backend - Already Done! ✅

The backend automatically:
  • Sends FCM notifications when createNotification() is called
  • Cleans up invalid/expired tokens
  • Handles multiple devices per user
  • Logs delivery results
No additional backend work needed!

Troubleshooting

”No FCM tokens found for user”

  • User hasn’t registered their device yet
  • Check that token registration is called after login

”Permission not granted”

  • User denied notification permissions
  • Re-request permissions or guide user to settings

Notifications not appearing

  • Verify token is registered (check Firestore user document)
  • Check Firebase Console logs for errors
  • Ensure physical device (not simulator for iOS)
  • For iOS: Verify APNs certificate is configured

Token registration fails

  • Check network connectivity
  • Verify user is authenticated
  • Check API endpoint is accessible

Monitoring

Check these for notification delivery:
  1. Firebase Console
    • Cloud Functions → Logs
    • Look for “FCM notification sent” messages
  2. User Documents in Firestore
    • Navigate to users/{userId}
    • Check fcmTokens field
    • Verify tokens are present
  3. Backend Logs
    • Watch for “Failed to send push notification” errors
    • Check token cleanup messages

Support

  • Full documentation: docs/engineering/notifications/fcm-push-notifications.mdx
  • Current in-app system: docs/engineering/notifications/notifications-structure.mdx
  • Email notifications: docs/engineering/notifications/email-notifications.mdx

Summary

Backend: Complete - FCM automatically sends when notifications are created ✅ API Endpoints: Complete - Token registration/removal endpoints ready ✅ Frontend Hooks: Complete - React hooks ready for mobile app ⏳ Mobile Integration: Next step - Add to React Native/Expo app Your notification system now supports:
  • ✅ In-app notifications (existing)
  • ✅ Email notifications (existing)
  • ✅ Native push notifications (NEW!)