The notification including the sound
import * as FirebaseAdmin from 'firebase-admin';
export class PushNotificationService {
private readonly logger = new Logger(PushNotificationService.name);
async sendNotifications(
tokens: string[] = [],
title = '',
body = '',
extras: Record<string, string> = {},
soundName: string = 'default' // e.g., 'incoming_ride' (no extension)
) {
try {
const isDefaultSound = soundName === 'default';
const androidSound = isDefaultSound ? 'default' : `${soundName}.mp3`;
const iosSound = isDefaultSound ? 'default' : `${soundName}.caf`;
const message = {
notification: {
title,
body,
},
data: extras,
android: {
priority: 'high' as const,
notification: {
sound: androidSound,
channelId: 'high_importance_channel',
},
},
apns: {
payload: {
aps: {
contentAvailable: true,
sound: iosSound,
},
},
},
webpush: {
headers: {
Urgency: 'high',
},
},
};
const batchSize = 400;
const tokenBatches: string[][] = [];
for (let i = 0; i < tokens.length; i += batchSize) {
tokenBatches.push(tokens.slice(i, i + batchSize));
}
for (const batch of tokenBatches) {
const response = await FirebaseAdmin.messaging().sendEachForMulticast({
tokens: batch,
...message,
});
this.logger.log(`Sent to batch with ${batch.length} tokens.`);
if (response.failureCount > 0) {
this.logger.warn(`Failures in batch: ${response.failureCount}`);
}
}
} catch (error: any) {
this.logger.error(`FCM error: ${error.message}`, error.stack);
}
}
}
implementation
tokens,
'Incoming Ride Request',
'You have a new ride request!',
{ rideId: 'xyz123' },
'incoming_ride' // No extension needed
);