Add Push Notifications to Existing App
Integrate Firebase Cloud Messaging so users receive real push notifications and tokens are persisted in the backend.
The route
7 steps to Done
- 01
Create Firebase Project & Obtain Credentials
Provide the cloud messaging service and keys needed for both client and server.
Preview prompt + verify gate ▾ Hide ▴
1. Log in to Firebase console and create a new project named after the existing app. 2. In the project settings, add an Android app (package ID from the React Native app) and download the generated google-services.json file. 3. Copy the web app config (apiKey, authDomain, projectId, storageBucket, messagingSenderId, appId) into a JSON snippet. 4. In Cloud Messaging settings, locate the Server key (legacy) and the Sender ID. 5. Return a JSON object containing: { "googleServicesFile": "<base64‑encoded google-services.json>", "firebaseConfig": {…}, "serverKey": "<FCM Server Key>", "senderId": "<Messaging Sender ID>" } No placeholders; include real base64 content and keys.
- ✓ Credentials JSON includes googleServicesFile, firebaseConfig, serverKey, and senderId.
- ✓ Values are non‑empty and look like real keys (e.g., serverKey starts with AAA...).
- ✓ Base64 string decodes to a valid google‑services.json.
- 02
Add FCM Client Library and Configure Android Build
Install the messaging SDK and integrate the downloaded google‑services.json into the React Native project.
Preview prompt + verify gate ▾ Hide ▴
1. Run `npm install @react-native-firebase/app @react-native-firebase/messaging`. 2. Decode the base64 google‑services.json from step 1 and place it at android/app/google-services.json. 3. In android/build.gradle, add `classpath 'com.google.gms:google-services:4.4.0'` inside the dependencies block. 4. In android/app/build.gradle, apply the plugin at the bottom: `apply plugin: 'com.google.gms.google-services'`. 5. Ensure `minSdkVersion` is >= 21. 6. Commit these changes to the repo. 7. Return a brief summary of the file modifications with line numbers where changes were made. No placeholder code; the file paths must be exact.
- ✓ google‑services.json is present at android/app/
- ✓ Both build.gradle files contain the new lines as described.
- ✓ npm packages appear in package.json with concrete version numbers.
- 03
Implement Permission Request & Token Retrieval on Device
Obtain the device’s FCM token and request user permission, then send the token to the backend for persistence.
Preview prompt + verify gate ▾ Hide ▴
Create a new file src/services/pushService.js containing: 1. An async function requestPermission() that calls messaging().requestPermission() and returns true/false. 2. An async function getToken() that calls messaging().getToken() and returns the token string. 3. An async function registerToken(userId) that: a. Calls requestPermission(); if denied, log and exit. b. Calls getToken(); if null, log error. c. Retrieves the auth token from AsyncStorage key 'authToken'. d. Sends a POST to `${BASE_URL}/api/register-token` with headers Authorization: `Bearer ${authToken}` and JSON body {userId, token}. 4. Export registerToken. Add a call to registerToken(currentUser.id) inside the root component’s useEffect (once on mount). Return the complete file content and indicate where you added the useEffect call. No placeholder URLs; use the actual BASE_URL variable already defined in the project.
- ✓ Permission dialog appears on first run.
- ✓ Console logs show a valid token string.
- ✓ Network tab shows a POST to /api/register-token with correct JSON.
- 04
Create Backend Endpoint to Persist Tokens
Store each user's FCM token in PostgreSQL so notifications can be targeted later.
Preview prompt + verify gate ▾ Hide ▴
1. Create a new migration (SQL) to add a table `device_tokens` with columns: id SERIAL PRIMARY KEY, user_id INTEGER REFERENCES users(id) ON DELETE CASCADE, token VARCHAR NOT NULL UNIQUE, created_at TIMESTAMP DEFAULT NOW(). 2. In the Express app, add a route: ```js router.post('/api/register-token', authMiddleware, async (req, res) => { const { userId, token } = req.body; if (parseInt(userId) !== req.user.id) return res.status(403).json({error:'User mismatch'}); await db.query('INSERT INTO device_tokens (user_id, token) VALUES ($1, $2) ON CONFLICT (token) DO UPDATE SET user_id = $1', [userId, token]); res.json({status:'ok'}); }); ``` 3. Export the route and ensure it's mounted. 4. Return the SQL migration content and the route code snippet. No placeholders; use actual table/column names.
- ✓ device_tokens table present with id, user_id, token, created_at.
- ✓ POST /api/register-token returns {status:'ok'} for valid auth.
- ✓ Invalid userId triggers 403 response.
- 05
Build Server‑Side Push Sender Service
Provide an endpoint that triggers an FCM push to a specific user’s devices.
Preview prompt + verify gate ▾ Hide ▴
1. In `src/routes/notifications.js`, add a new route: ```js router.post('/api/send-notification', authMiddleware, async (req, res) => { const { userId, title, body } = req.body; const tokensResult = await db.query('SELECT token FROM device_tokens WHERE user_id = $1', [userId]); const tokens = tokensResult.rows.map(r => r.token); if (!tokens.length) return res.status(404).json({error:'No device tokens'}); const message = { notification: { title, body }, registration_ids: tokens }; const fcmResponse = await fetch('https://fcm.googleapis.com/fcm/send', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `key=${process.env.FCM_SERVER_KEY}` }, body: JSON.stringify(message) }); const result = await fcmResponse.json(); res.json(result); }); ``` 2. Ensure `process.env.FCM_SERVER_KEY` is set (use dotenv). 3. Return the full route code snippet. No dummy URLs; the fetch must target the real FCM endpoint.
- ✓ POST /api/send-notification returns FCM response JSON.
- ✓ Devices receive a notification with the given title and body.
- ✓ Non‑admin requests are rejected with 403.
- 06
Add In‑App Test Notification UI
Provide a simple screen for admins to send a test push and verify end‑to‑end flow.
Preview prompt + verify gate ▾ Hide ▴
1. Add a new file `src/screens/AdminPushScreen.js` containing a functional component with: - Two TextInput fields bound to state variables `title` and `body`. - A Button labeled "Send Test". - On press, call `fetch(`${BASE_URL}/api/send-notification`, { method:'POST', headers:{ 'Content-Type':'application/json', 'Authorization': `Bearer ${authToken}` }, body: JSON.stringify({ userId: currentUser.id, title, body }) })`. - Display a toast or Alert with the response JSON or error. 2. Register this screen in the navigation (e.g., `AdminStackNavigator`). 3. Add a menu item "Test Push" in the admin drawer that navigates to AdminPushScreen. 4. Return the full component code and the navigation addition diff. No placeholder strings; use actual variable names from the project (e.g., `BASE_URL`, `authToken`).
- ✓ Admin screen appears in the drawer.
- ✓ Entering title/body and pressing Send results in a network request.
- ✓ Device shows the notification matching the entered title/body.
- 07
Validate End‑to‑End Delivery
Confirm that a token is stored, the send endpoint works, and the device receives the push.
Preview prompt + verify gate ▾ Hide ▴
1. Launch the app on a physical Android device. 2. On first launch, accept the permission prompt. 3. Verify in the backend database that a row exists in `device_tokens` for your user ID. 4. Open the admin drawer, select "Test Push", enter "Test Title" and "Hello World", press Send. 5. Observe the device’s notification shade: a notification with title "Test Title" and body "Hello World" should appear. 6. Capture screenshots of the permission dialog, DB row (e.g., via psql query), and the received notification. 7. Return a short report confirming each step succeeded. No placeholders; include the actual SQL query used.
- ✓ device_tokens table contains the test device token.
- ✓ Admin UI reports a successful FCM response.
- ✓ Device shows the notification with exact title/body.