firebase-electron-v2
v2.0.8
Published
Receive Firebase notifications in your Electron app using the Firebase JS SDK and Web Push APIs
Maintainers
Readme
firebase-electron-v2
Receive Firebase push notifications in your Electron app using the modern Firebase JS SDK (v9+) and Web Push APIs.
Installation
npm i firebase-electron-v2Usage
This library separates setup between Electron's main and renderer processes.
In the main process (main.js or main.ts)
The main process setup is minimal. It primarily prepares to listen for events forwarded from the renderer process via IPC.
import { app, BrowserWindow } from 'electron';
import { setup as setupPushReceiver } from 'firebase-electron';
let mainWindow: BrowserWindow | null;
app.on('ready', () => {
mainWindow = new BrowserWindow({
// ... your window options
webPreferences: {
// Ensure you have appropriate webPreferences for IPC and potentially contextIsolation
// preload: path.join(__dirname, 'preload.js') // Example if using preload
nodeIntegration: false, // Recommended: false
contextIsolation: true, // Recommended: true
// Service workers require secure origins (https or localhost) or custom protocols
},
});
// Option 1: Serve your index.html via a secure context
// mainWindow.loadURL('https://localhost:3000'); // Example using a local dev server
// Option 2: Or register a custom protocol
// protocol.registerFileProtocol('app', (request, callback) => { /* ... */ });
// mainWindow.loadURL('app://./index.html');
// Call setup *after* creating the window but *before* loading content usually works best.
if (mainWindow) {
setupPushReceiver(mainWindow.webContents);
}
// ... rest of your main process setup
});In the renderer process (renderer.js or renderer.ts)
This is where the core Firebase messaging initialization happens.
// Assuming you have exposed ipcRenderer.send via contextBridge in your preload script
// Example preload.ts:
// import { contextBridge, ipcRenderer } from 'electron';
// contextBridge.exposeInMainWorld('electronIpc', {
// send: (channel, ...args) => ipcRenderer.send(channel, ...args),
// // You might also need to expose ipcRenderer.on for receiving events
// on: (channel, listener) => {
// const subscription = (event, ...args) => listener(...args);
// ipcRenderer.on(channel, subscription);
// return () => ipcRenderer.removeListener(channel, subscription); // Return cleanup function
// }
// });
// In renderer.ts:
declare global {
interface Window {
electronIpc: {
send: (channel: string, ...args: any[]) => void;
on: (channel: string, listener: (...args: any[]) => void) => () => void;
};
}
}
// Import the initialization function from the renderer entry point
import { initializeFirebaseMessaging, type IpcSender } from 'firebase-electron-v2/renderer';
// Import constants from the specific constants entry point
import {
NOTIFICATION_RECEIVED,
TOKEN_UPDATED,
// NOTIFICATION_SERVICE_ERROR, // Optional: Listen for errors forwarded from main if needed
} from 'firebase-electron/dist/electron/consts'; // Note the specific path
// Your Firebase project configuration (including VAPID key)
const firebaseCredentials = {
apiKey: 'YOUR_API_KEY',
authDomain: 'YOUR_AUTH_DOMAIN',
projectId: 'YOUR_PROJECT_ID',
storageBucket: 'YOUR_STORAGE_BUCKET',
messagingSenderId: 'YOUR_MESSAGING_SENDER_ID',
appId: 'YOUR_APP_ID',
vapidKey: 'YOUR_VAPID_KEY',
};
// Create the ipcSender object using the exposed API from preload script
const ipcSender: IpcSender = {
send: window.electronIpc.send, // Pass the actual send function
};
// Call initializeFirebaseMessaging to start the process, passing credentials AND the ipcSender
initializeFirebaseMessaging(firebaseCredentials, ipcSender)
.then(() => {
console.log('Firebase Messaging setup initiated in renderer.');
})
.catch((err) => {
console.error('Error initiating Firebase Messaging setup:', err);
});
// Listen for the updated FCM token (using the exposed 'on' method from preload)
const unsubscribeTokenUpdates = window.electronIpc.on(TOKEN_UPDATED, (token) => {
console.log('FCM Token received in renderer:', token);
// *** IMPORTANT: Send this token to your application server ***
// Example: sendTokenToServer(token);
});
// Handle notifications received while the app is in the foreground (using the exposed 'on' method)
const unsubscribeNotifications = window.electronIpc.on(NOTIFICATION_RECEIVED, (notificationPayload) => {
console.log('Notification received in foreground:', notificationPayload);
// Display the notification using an in-app UI or the Notification API
// Example: new Notification(notificationPayload.title, { body: notificationPayload.body });
});
// Optional: Listen for errors forwarded from the main process setup
// const unsubscribeErrors = window.electronIpc.on(NOTIFICATION_SERVICE_ERROR, (error) => {
// console.error('Error reported from main process push receiver setup:', error);
// });
// Remember to clean up listeners when the component unmounts or window closes
// window.addEventListener('beforeunload', () => {
// unsubscribeTokenUpdates();
// unsubscribeNotifications();
// unsubscribeErrors(); // If listening for errors
// });Service Worker (firebase-messaging-sw.js)
This library relies on a Service Worker to handle push notifications when your application is in the background or closed.
- Location: The
firebase-messaging-sw.jsfile must be served from the root of your application (e.g.,https://your-app.com/firebase-messaging-sw.jsor accessible at/on your local development server). This library's build process places the file indist/firebase-messaging-sw.js. You need to configure your Electron app's build or server setup to copy and serve this file from the root. - Credentials: The service worker runs independently and needs its own copy of the Firebase configuration. You must configure this. Common methods:
- Build-Time Substitution: Use build tools (like Webpack DefinePlugin, Vite env variables) to replace placeholders in the service worker file during your application's build process.
- Fetch Configuration: Have the service worker fetch a JSON configuration file (e.g.,
/firebase-config.json) that you serve alongside your application.
The library's dist/firebase-messaging-sw.js contains placeholders like PLACEHOLDER_API_KEY. You need to modify it or use a build process to inject the correct values.
Example firebase-messaging-sw.js (after replacing placeholders):
// firebase-messaging-sw.js (ensure this is served from your app root)
import { initializeApp } from 'firebase/app';
import { getMessaging, onBackgroundMessage } from 'firebase/messaging/sw';
// *** Replace these with your actual credentials (e.g., via build process) ***
const firebaseConfig = {
apiKey: 'YOUR_ACTUAL_API_KEY',
authDomain: 'YOUR_ACTUAL_AUTH_DOMAIN',
projectId: 'YOUR_ACTUAL_PROJECT_ID',
storageBucket: 'YOUR_ACTUAL_STORAGE_BUCKET',
messagingSenderId: 'YOUR_ACTUAL_SENDER_ID',
appId: 'YOUR_ACTUAL_APP_ID',
};
// Initialize Firebase
try {
const app = initializeApp(firebaseConfig);
const messaging = getMessaging(app);
onBackgroundMessage(messaging, (payload) => {
console.log('[firebase-messaging-sw.js] Received background message ', payload);
const notificationTitle = payload.notification?.title || 'Background Message';
const notificationOptions = {
body: payload.notification?.body || '',
icon: payload.notification?.icon || '/default-icon.png', // Provide a default icon
};
// Use self.registration to show the notification
self.registration.showNotification(notificationTitle, notificationOptions);
});
} catch (e) {
console.error('Error initializing Firebase in SW:', e);
}
// Optional: Add notification click handler
self.addEventListener('notificationclick', (event) => {
event.notification.close();
console.log('Notification clicked:', event.notification);
// Add logic here to open/focus a window, e.g.:
// event.waitUntil(
// clients.matchAll({ type: 'window', includeUncontrolled: true }).then(windowClients => {
// // Check if a window is already open
// for (let client of windowClients) {
// if (client.url === '/' && 'focus' in client) { // Adjust URL check as needed
// return client.focus();
// }
// }
// // If not, open a new window
// if (clients.openWindow) {
// return clients.openWindow('/'); // Adjust URL to open
// }
// })
// );
});Where to find appId, apiKey, projectId and vapidKey
- Go to Firebase Console & login to your account
- Select your project
- Click on the
Project Settingscog icon - Click on
Project Settings - Make sure you're on the
Generaltab - Scroll down to the
Your appssection - If you don't have a Web app, click on
Add app- Select
Web(</>) icon - Fill in the required fields
- Click on
Register app
- Select
- Copy the configuration object values (
apiKey,authDomain,projectId,storageBucket,messagingSenderId,appId) shown under theSDK setup and configurationsection (select Config or CDN). - Go to the
Cloud Messagingtab. - Under
Web configuration>Web Push certificates, clickGenerate key pairif you don't have one. - Copy the value in the
Key paircolumn. This is yourvapidKey.
Moving from electron-push-receiver
electron-push-receiver used the Legacy FCM API which is deprecated and being shut down.
This package (firebase-electron-v2) is a refactored solution using the modern Firebase JS SDK (v9+) that relies on standard Web Push APIs via a Service Worker.
Key Differences & Migration Steps:
- Architecture: The core logic now resides in the renderer process and a service worker, not the main process.
- Main Process: The
setupfunction (imported fromfirebase-electron-v2) is still called in the main process, but it primarily sets up IPC listeners. - Renderer Process:
- You must import
initializeFirebaseMessagingfromfirebase-electron-v2/renderer. - Call
initializeFirebaseMessaging(firebaseCredentials)instead of sendingSTART_NOTIFICATION_SERVICEvia IPC. - Listen for
TOKEN_UPDATEDviaipcRenderer.onto get the FCM token and send it to your server. - Listen for
NOTIFICATION_RECEIVEDviaipcRenderer.onto handle foreground messages.
- You must import
- Service Worker:
- You must create/configure and serve the
firebase-messaging-sw.jsfile (provided indist/) from your application root. - You must provide Firebase credentials within this service worker file (e.g., via a build step).
- You must create/configure and serve the
- Credentials: You now need the full Firebase Web config (
apiKey,authDomain,projectId, etc.) plus thevapidKeyin your renderer process.
Credits to Matthieu Lemoine and contributors for the original electron-push-receiver.
What's new (in this fork/rewrite)
- Uses modern Firebase JS SDK (v9+) and standard Web Push APIs.
- Relies on a Service Worker for background notifications.
- Clear separation between main (
setup) and renderer (initializeFirebaseMessaging) initialization. - Uses updated dependencies.
- Removed legacy dependencies (
electron-config,request,protobufjs, etc.). - Simplified core logic.
- Requires Node.js >= 18 (as required by modern Firebase SDK).
- Uses
tsupfor building andvitestfor testing. - Completely written in TypeScript.
[!CAUTION] > Breaking changes: Requires full Firebase Web config + VAPID key. Initialization logic moved to the renderer. Requires manual setup and configuration of the Service Worker (
firebase-messaging-sw.js).
Development
- Make sure you have Node.js >= v18 installed.
- Install dependencies with
npm install. - If running tests that require credentials, create a
.envfile from.env.templateand fill in your Firebase project details. - Build with
npm run build. - Run tests with
npm run test.
[!NOTE] Currently, the automated tests (
npm run test) are not implemented for the refactored codebase. Contributions are welcome!
