I am trying to send notification in react native via rest api. In response i'm getting success :1. The problem here is i'm not getting notification in emulator.
const token = await firebase.messaging().getToken();
console.log("token in sendNotification ",token)
const FIREBASE_API_KEY = "firebaseApiKey";
const message = {
to :token,
notification: {
title: "This is a Notification",
boby: "This is the body of the Notification",
vibrate: 1,
sound: 1,
show_in_foreground: true,
priority: "high",
content_available: true,
}
}
let headers = new Headers({
"Content-Type" : "application/json",
Authorization: "key=" + FIREBASE_API_KEY,
})
try {
let response = await fetch ("https://fcm.googleapis.com/fcm/send",{
method: "POST",
headers,
body: JSON.stringify(message),
})
response = await response.json();
console.log("response ", response);
} catch (error) {
console.log("error ", error);
}
https://firebase.google.com/docs/cloud-messaging/js/receive
You can follow the incoming message documentation
// Handle incoming messages. Called when:
// - a message is received while the app has a focus
// - the user clicks on an app notification created by a service worker
// `messaging.setBackgroundMessageHandler` handler.
messaging.onMessage((payload) => {
console.log('Message received. ', payload);
// ...
});
In useEffect of App.js
import React,{useEffect} from 'react'
import{View,Text} from 'react-native';
async function onDisplayNotification() {
// Create a channel
const channelId = await notifee.createChannel({
id: 'default',
name: 'Default Channel',
});
// Display a notification
await notifee.displayNotification({
title: 'Notification Title',
body: 'Main body content of the notification',
android: {
channelId,
smallIcon: 'ic_launcher', // optional, defaults to 'ic_launcher'.
},
});
}
const App=()=> {
useEffect(() => {
// Assume a message-notification contains a "type" property in the data payload of the screen to open
messaging().onMessage((payload) => {
console.log('Message received. ', payload);
onDisplayNotification();
// ...
});
messaging().onNotificationOpenedApp(remoteMessage => {
console.log(
'Notification caused app to open from background state:',
remoteMessage.notification,
);
alert("Notification");
});
// Check whether an initial notification is available
messaging()
.getInitialNotification()
.then(remoteMessage => {
if (remoteMessage) {
console.log(
'Notification caused app to open from quit state:',
remoteMessage.notification,
);
}
});
}, []);
return (
<View>
</View>
)
}
export default App;
Related
Platform: React Native (IOS target)
Hi,
I am trying to build a way to send push notifications ( it's basically an app simillar to any datting app where you click a user and it sends them a notification to let them know I want to connect).
The notifications are being received fine on Expo Go but I don't know how to target a specific individual? Do I need backend for this?
This is my code:
const ProfilesScreen: React.FC<Props> = ({ navigation, route }) => {
const [expoPushToken, setExpoPushToken] = useState("");
const [notification, setNotification] = useState(false);
const [notificationRes, setNotifcationRes] = useState([]);
const notificationListener = useRef();
const responseListener = useRef();
const [data, setData] = useState<any>([]);
const [notificationsSentUids, setNotificationSentUids] = useState<any>([]);
const [notificationsCount, setNotificationsCount] = useState<number>(0);
const [activeIndex, setActiveIndex] = useState<number>(0);
const [storageData, setStorageData] = useState({});
async function sendPushNotification(expoPushToken) {
const message = {
to: expoPushToken,
sound: "default",
title: "My message",
body: storageData
? `${storageData?.name} wants to connect with you`
: null,
data: { someData: "goes here" },
};
await fetch("https://exp.host/--/api/v2/push/send", {
method: "POST",
headers: {
Accept: "application/json",
"Accept-encoding": "gzip, deflate",
"Content-Type": "application/json",
},
body: JSON.stringify(message),
});
}
async function registerForPushNotificationsAsync() {
let token;
if (Device.isDevice) {
const { status: existingStatus } =
await Notifications.getPermissionsAsync();
let finalStatus = existingStatus;
if (existingStatus !== "granted") {
const { status } = await Notifications.requestPermissionsAsync();
finalStatus = status;
}
if (finalStatus !== "granted") {
alert("Failed to get push token for push notification!");
return;
}
token = (await Notifications.getExpoPushTokenAsync()).data;
setExpoPushToken(token);
} else {
// alert("Must use physical device for Push Notifications");
}
return token;
}
useEffect(() => {
registerForPushNotificationsAsync().then(token => setExpoPushToken(token));
// This listener is fired whenever a notification is received while the app is foregrounded
notificationListener.current =
Notifications.addNotificationReceivedListener(notification => {
setNotification(notification);
});
// This listener is fired whenever a user taps on or interacts with a notification (works when app is foregrounded, backgrounded, or killed)
responseListener.current =
Notifications.addNotificationResponseReceivedListener(response => {
setNotifcationRes(response);
return () => {
Notifications.removeNotificationSubscription(
notificationListener.current
);
Notifications.removeNotificationSubscription(responseListener.current);
};
}, []);
return ....
};
I need your help, all looks good but I cannot receive notification on my react-native application.
Actual setup:
In project.xcworkspace, TARGETS > {App Name} > Signing & Capabilities :
Background Modes: Background fetch and Remote notifications enabled
Push Notifications
Firebase Project:
APNs key was added, with KeyId, TeamId
Apple Developer:
Identifier added with Push Notifications enabled
Key added with "Apple Push Notifications service (APNs)"
Profiles > I created a "Provisioning Profile" to try if I have a connection issue(not resolved with this)
Good to know: I actually use the firebase authentification and it works so I don't think it's a GoogleService-Info.plist issue.
App.js
const requestNotificationPermission = async () => {
messaging()
.requestPermission()
.then(authStatus => {
console.log('APNs Status: ', authStatus);
if (
authStatus === messaging.AuthorizationStatus.AUTHORIZED ||
authStatus == messaging.AuthorizationStatus.PROVISIONAL
) {
messaging()
.getToken()
.then(async (token) => {
await messaging().registerDeviceForRemoteMessages()
.then((result) => {
console.log('Device registered for remote messages: ', result);
});
console.log('Token', token);
});
messaging().onTokenRefresh(token => {
console.log('Refresh Token: ', token)
});
}
})
.catch(err => {
console.log('Error: ', err);
});
}
React.useEffect(() => {
requestNotificationPermission();
const unsubscribe = messaging().onMessage(async remoteMessage => {
Alert.alert('A new FCM message arrived!', JSON.stringify(remoteMessage));
})
return unsubscribe;
}, [])
Return :
LOG APNs Status: 1
LOG Device registered for remote messages: true
LOG Token fmORQaQ0FUCjnGA1TgXSDi:APA91bFVDMvgkY13Rl-muzI2kOKCJauFcJCVF7sZZxgnuicawTIcvrl73JtNUEruobDfu1igvgVkXobi3gUTvGXD1QMZoOskXUzAEkDJpMgUvug-9KudE6bJl9oBL0tJCc9Eqv0GXfXa
I tried to create a notification from https://console.firebase.google.com/u/0/project/proj-number/messaging
Result: Nothing append on react-native app.
I tried to create a notification from REST
REST Request Setup :
Method: POST
Url : https://fcm.googleapis.com/fcm/send
Headers > Authorization > key={Cloud Messaging > Server Key}
Body :
{
"data": {},
"notification": {
"body": "This is an FCM notification",
"title": "From Postman"
},
"to": "fmORQaQ0FUCjnGA1TgX[...]"
}
Postman Result :
{
"multicast_id": 5211676236934629976,
"success": 1,
"failure": 0,
"canonical_ids": 0,
"results": [
{
"message_id": "0:1660579924324918%fedf4cbefedf4cbe"
}
]
}
React-native result : Nothing append on react-native app.
Resolved:
App.js
const getToken = () => {
messaging()
.getToken()
.then(x => console.log(x))
.catch(e => console.log(e));
};
const registerForRemoteMessages = () => {
messaging()
.registerDeviceForRemoteMessages()
.then(() => {
console.log('Registered');
requestPermissions();
})
.catch(e => console.log(e));
};
const requestPermissions = () => {
messaging()
.requestPermission()
.then((status) => {
if (status === 1) {
console.log('Authorized');
onMessage();
} else {
console.log('Not authorized');
}
})
.catch(e => console.log(e));
};
const onMessage = () => {
messaging()
.onMessage(response => {
console.log(response.data.notification);
});
};
React.useEffect(() => {
getToken();
if (Platform.OS === 'ios') {
registerForRemoteMessages();
} else {
onMessage();
}
const unsubscribe = messaging().onMessage(async remoteMessage => {
Alert.alert('A new FCM message arrived!', JSON.stringify(remoteMessage));
})
return unsubscribe;
}, [])
I have the following Function that:
Listens for document (text message) creation
Grab IDs of members of a group chat
Get the FCM Tokens for each member
With a for-loop, send messages to group members
exports.sendChatMessage = functions.firestore
.document("chats/{mealID}/messages/{messageID}")
.onCreate((snap, context) => {
const data = snap.data();
const mealID = context.params.mealID;
const senderID = data.senderID;
const senderName = data.senderName;
const messageContent = data.content;
var docRef = db.collection("chats").doc(mealID);
docRef
.get()
.then((doc) => {
if (doc.exists) {
const docData = doc.data();
const mealName = docData.name;
const userStatus = docData.userStatus;
var users = docData.to;
var eligibleUsers = users.filter(
(user) => userStatus[user] == "accepted"
);
eligibleUsers.push(docData.from);
// get fcmTokens from eligibleUsers and send the messagme
db.collection("users")
.where("uid", "in", eligibleUsers)
.get()
.then((snapshot) => {
var fcmTokens = [];
var thumbnailPicURL = "";
// get thumbnailpic of the sender and collect fcmTokens
snapshot.forEach((doc) => {
if (doc.data().uid == senderID) {
thumbnailPicURL =
doc.data().thumbnailPicURL == null
? "https://i.imgur.com/8wSudUk.png"
: doc.data().thumbnailPicURL;
} else {
fcmTokens.push(doc.data().fcmToken);
}
});
// send the message fcmTokens
fcmTokens.forEach((token) => {
if (token != "") {
const fcmMessage = {
message: {
token: token,
notification: {
title: mealName,
body: senderName + ": " + messageContent,
image: thumbnailPicURL,
},
apns: {
payload: {
aps: {
category: "MESSAGE_RECEIVED",
},
MEAL_ID: mealID,
},
},
},
};
tokenManger.sendFcmMessage(fcmMessage);
}
});
return true;
});
} else {
// doc.data() will be undefined in this case
console.log("No such document!");
return false;
}
})
.catch((error) => {
console.log("Error getting document:", error);
return false;
});
return true;
});
My send function comes from a helper file that uses the HTTP V1 protocol to build the send-request:
const { google } = require("googleapis");
const https = require("https");
const MESSAGING_SCOPE = "https://www.googleapis.com/auth/firebase.messaging";
const SCOPES = [MESSAGING_SCOPE];
const PROJECT_ID = MY_PROJECT_ID;
const HOST = "fcm.googleapis.com";
const PATH = "/v1/projects/" + PROJECT_ID + "/messages:send";
exports.getAccessToken = () => {
return new Promise(function (resolve, reject) {
const key = require("./service-account.json");
var jwtClient = new google.auth.JWT(
key.client_email,
null,
key.private_key,
SCOPES,
null
);
jwtClient.authorize(function (err, tokens) {
if (err) {
reject(err);
return;
}
resolve(tokens.access_token);
});
});
};
//send message
exports.sendFcmMessage = (fcmMessage) => {
this.getAccessToken().then(function (accessToken) {
var options = {
hostname: HOST,
path: PATH,
method: "POST",
headers: {
Authorization: "Bearer " + accessToken,
},
// … plus the body of your notification or data message
};
var request = https.request(options, function (resp) {
resp.setEncoding("utf8");
resp.on("data", function (data) {
console.log("Message sent to Firebase for delivery, response:");
console.log(data);
});
});
request.on("error", function (err) {
console.log("Unable to send message to Firebase");
console.log(err);
});
request.write(JSON.stringify(fcmMessage));
request.end();
});
};
It worked all fine in the emulator but once deployed, there're significant delays (~3 mins):
I also noticed that the console says the cloud function finishes execution BEFORE sendFcmMessage logs success messages.
I did some research online, it appears that it might have something to do with the usage of Promise but I wasn't sure if that's the sole reason or it has something to do with my for-loop.
The Problem
To summarize the issue, you are creating "floating promises" or starting other asynchronous tasks (like in sendFcmMessage) where you aren't returning a promise because they use callbacks instead.
In a deployed function, as soon as the function returns its result or the Promise chain resolves, all further actions should be treated as if they will never be executed as documented here. An "inactive" function might be terminated at any time, is severely throttled and any network calls you make (like setting data in database or calling out to FCM) may never be executed.
An indicator that you haven't properly chained the promises is when you see the function completion log message ("Function execution took...") before other messages you are logging. When you see this, you need to look at the code you are running and confirm whether you have any "floating promises" or are using callback-based APIs. Once you have changed the callback-based APIs to use promises and then made sure they are all chained together properly, you should see a significant boost in performance.
The fixes
Sending the message data to FCM
In your tokenManger file, getAccessToken() could be reworked slightly and sendFcmMessage should be converted to return a Promise:
exports.getAccessToken = () => {
return new Promise(function (resolve, reject) {
const key = require("./service-account.json");
const jwtClient = new google.auth.JWT(
key.client_email,
null,
key.private_key,
SCOPES,
null
);
jwtClient.authorize(
(err, tokens) => err ? reject(err) : resolve(tokens.access_token)
);
});
};
//send message
exports.sendFcmMessage = (fcmMessage) => {
// CHANGED: return the Promise
return this.getAccessToken().then(function (accessToken) {
const options = {
hostname: HOST,
path: PATH,
method: "POST",
headers: {
Authorization: "Bearer " + accessToken,
},
// … plus the body of your notification or data message
};
// CHANGED: convert to Promise:
return new Promise((resolve, reject) => {
const request = https.request(options, (resp) => {
resp.setEncoding("utf8");
resp.on("data", resolve);
resp.on("error", reject);
});
request.on("error", reject);
request.write(JSON.stringify(fcmMessage));
request.end();
});
});
};
However, the above code was built for googleapis ^52.1.0 and google-auth-library ^6.0.3. The modern versions of these modules are v92.0.0 and v7.11.0 respectively. This means you should really update the code to use these later versions like so:
// Import JWT module directly
const { JWT } = require('google-auth-library');
// FIREBASE_CONFIG is a JSON string available in Cloud Functions
const PROJECT_ID = JSON.parse(process.env.FIREBASE_CONFIG).projectId;
const FCM_ENDPOINT = `https://fcm.googleapis.com/v1/projects/${PROJECT_ID}/messages:send`;
const FCM_SCOPES = ["https://www.googleapis.com/auth/firebase.messaging"];
exports.sendFcmMessage = (fcmMessage) => {
const key = require("./service-account.json"); // consider moving outside of function (so it throws an error during deployment if its missing)
const client = new JWT({
email: key.client_email,
key: key.private_key,
scopes: FCM_SCOPES
});
return client.request({ // <-- this uses `gaxios`, Google's fork of `axios` built for Promise-based APIs
url: FCM_ENDPOINT,
method: "POST",
data: fcmMessage
});
}
Better yet, just use the messaging APIs provided by the Firebase Admin SDKs that handle the details for you. Just feed it the message and tokens as needed.
import { initializeApp } from "firebase-admin/app";
import { getMessaging } from "firebase-admin/messaging";
initializeApp(); // initializes using default credentials provided by Cloud Functions
const fcm = getMessaging();
fcm.send(message) // send to one (uses the given token)
fcm.sendAll(messagesArr) // send to many at once (each message uses the given token)
fcm.sendMulticast(message) // send to many at once (uses a `tokens` array instead of `token`)
The Cloud Function
Updating the main Cloud Function, you'd get:
exports.sendChatMessage = functions.firestore
.document("chats/{mealID}/messages/{messageID}")
.onCreate((snap, context) => {
const mealID = context.params.mealID;
const { senderID, senderName, content: messageContent } = snap.data();
const docRef = db.collection("chats").doc(mealID);
/* --> */ return docRef
.get()
.then((doc) => {
if (!doc.exists) { // CHANGED: Fail fast and avoid else statements
console.log(`Could not find "chat:${mealID}"!`);
return false;
}
const { userStatus, to: users, name: mealName, from: fromUser } = doc.data();
const eligibleUsers = users.filter(
(user) => userStatus[user] == "accepted"
);
eligibleUsers.push(fromUser);
// get fcmTokens from eligibleUsers and send the message
/* --> */ return db.collection("users")
.where("uid", "in", eligibleUsers) // WARNING: This will only work for up to 10 users! You'll need to break it up into chunks of 10 if there are more.
.get()
.then(async (snapshot) => {
const fcmTokens = [];
let thumbnailPicURL = "";
// get thumbnailpic of the sender and collect fcmTokens
snapshot.forEach((doc) => {
if (doc.get("uid") == senderID) {
thumbnailPicURL = doc.get("thumbnailPicURL"); // update with given thumbnail pic
} else {
fcmTokens.push(doc.get("fcmToken"));
}
});
const baseMessage = {
notification: {
title: mealName,
body: senderName + ": " + messageContent,
image: thumbnailPicURL || "https://i.imgur.com/8wSudUk.png", // CHANGED: specified fallback image here
},
apns: {
payload: {
aps: {
category: "MESSAGE_RECEIVED",
},
MEAL_ID: mealID,
},
}
}
// log error if fcmTokens empty?
// ----- OPTION 1 -----
// send the message to each fcmToken
const messagePromises = fcmTokens.map((token) => {
if (!token) // handle "" and undefined
return; // skip
/* --> */ return tokenManger
.sendFcmMessage({
message: { ...baseMessage, token }
})
.catch((err) => { // catch the error here, so as many notifications are sent out as possible
console.error(`Failed to send message to "fcm:${token}"`, err);
})
});
await Promise.all(messagePromises); // wait for all messages to be sent out
// --------------------
// ----- OPTION 2 -----
// send the message to each fcmToken
await getMessaging().sendAll(
fcmTokens.map((token) => ({ ...baseMessage, token }))
);
// --------------------
return true;
})
.catch((error) => {
console.log("Error sending messages:", error);
return false;
});
})
.catch((error) => {
console.log("Error getting document:", error);
return false;
});
});
I found out that the culprit is my queries to db. Like #samthecodingman commented, I was creating floating Promises.
Originally, I have codes like:
db.collection("users")
.where("uid", "in", eligibleUsers)
.get()
.then((snapshot) => {...}
All I needed to do is to return that call:
return db.collection("users")
.where("uid", "in", eligibleUsers)
.get()
.then((snapshot) => {...}
Although it's still not instant delivery, it's much faster now.
EDITED onMessageSent function. Still the same error.
I'm trying to enable push notifications in flutter and I am using Firebase Messaging. I am encountering the following issue. There are two, namely "onCreateActivityFeedItem" & "onMessageSent".
For the first one, "onCreateActivityFeedItem", the notification feature is perfectly fine, but I could not recognize the problem with the second one. Please do help.
The problem I am facing :
onMessageSent
TypeError: Cannot read property 'androidNotificationToken' of undefined at exports.onMessageSent.functions.firestore.document.onCreate (/workspace/index.js:152:47) at process._tickCallback (internal/process/next_tick.js:68:7)
Here is the "onCreateActivityFeedItem" :
From my index.js
exports.onCreateActivityFeedItem = functions.firestore
.document("/feed/{userId}/feedItems/{activityFeedItem}")
.onCreate(async (snapshot, context) => {
console.log("Activity Feed Item Created", snapshot.data());
// 1) Get user connected to the feed
const userId = context.params.userId;
const mediaUrl=context.params.mediaUrl;
const userRef = admin.firestore().doc(`users/${userId}`);
const doc = await userRef.get();
// 2) Once we have user, check if they have a notification token; send notification, if they have a token
const androidNotificationToken = doc.data().androidNotificationToken;
const createdActivityFeedItem = snapshot.data();
if (androidNotificationToken) {
sendNotification(androidNotificationToken, createdActivityFeedItem);
} else {
console.log("No token for user, cannot send notification");
}
function sendNotification(androidNotificationToken, activityFeedItem) {
let body;
// 3) switch body value based off of notification type
switch (activityFeedItem.type) {
case "comment":
body = `${activityFeedItem.username} replied: ${
activityFeedItem.commentData
}.`;
break;
case "like":
body = `${activityFeedItem.username} booped you.`;
break;
case "follow":
body = `${activityFeedItem.username} started petting you.`;
break;
default:
break;
}
// 4) Create message for push notification
const message = {
notification: {
body:body,
image:mediaUrl
},
token: androidNotificationToken,
data: {recipient: userId,
}
};
// 5) Send message with admin.messaging()
admin
.messaging()
.send(message)
.then(response => {
// Response is a message ID string
console.log("Successfully sent message", response);
return null;
})
.catch(error => {
console.log("Successfully sent message", response);
throw Error("Could not send message.",error)});
// admin.messaging().sendToDevice(androidNotificationToken,message);
}
});
From my index.js
Here is the, "onMessageSent":
exports.onMessageSent = functions.firestore
.document('/messages/{chatId}/messageInfo/{messageFeedItem}')
.onCreate(async (snapshot, context) => {
console.log("Message Created", snapshot.data());
// 1) Get user connected to the feed
const chatId=context.params.chatId;
const userId = context.params.idTo;
const idTo =context.params.idTo;
const userRef = admin.firestore().doc(`users/${idTo}`);
const doc = await userRef.get();
const createdMessageFeedItem = snapshot.data();
// 2) Once we have user, check if they have a notification token; send notification, if they have a token
const androidNotificationToken = doc.data().androidNotificationToken;
if (androidNotificationToken) {
sendNotification(androidNotificationToken, createdMessageFeedItem);
} else {
console.log("No token for user, cannot send notification");
}
function sendNotification(androidNotificationToken,createdMessageFeedItem) {
let body;
// 3) switch body value based off of notification type
switch (messageFeedItem.type) {
case 0:
body = `${messageFeedItem.username} has sent a message : ${
messageFeedItem.content
}.`;
break;
case 1:
body = `${messageFeedItem.username} has sent an image.`;
break;
case 2:
body = `${messageFeedItem.username} has sent a gif.`;
break;
default:
break;
}
// 4) Create message for push notification
const message = {
notification:
{body:body,},
token: androidNotificationToken,
data: {recipient: idTo,}
};
// 5) Send message with admin.messaging()
admin
.messaging()
.send(androidNotificationToken,message)
.then(response => {
// Response is a message ID string
console.log("Successfully sent message", response);
return null;
})
.catch(error => {
console.log("Successfully sent message", response);
throw Error("Could not send message.",error)});
// admin.messaging().sendToDevice(androidNotificationToken,message);
}
});
Where I called/declared the onMessage, onResume :
configurePushNotifications() {
final GoogleSignInAccount user = googleSignIn.currentUser;
if (Platform.isIOS) {
getiOSPermission();
}
_firebaseMessaging.getToken().then((token) {
print("Firebase messaging token : $token");
setState(() {
currentUser.androidNotificationToken = token;
});
usersref.doc(user.id).update({"androidNotificationToken": token});
});
_firebaseMessaging.configure(
onLaunch: (Map<String, dynamic> message) async {
_firebaseMessaging.getToken().then((token) {
print("Firebase messaging token : $token");
usersref.doc(user.id).update({"androidNotificationToken": token});
});
// print("On Launch : $message\n");
// _navigateToDetail(message);
},
onResume: (Map<String, dynamic> message) async {
_firebaseMessaging.getToken().then((token) {
print("Firebase messaging token : $token");
usersref.doc(user.id).update({"androidNotificationToken": token});
});
print("On Resume : $message");
_navigateToDetail(message);
},
onMessage: (Map<String, dynamic> message) async {
print("On message : $message\n");
final String recipientId = message['data']['recipient'];
final String body = message['notification']['body'];
if (recipientId == user.id) {
//Notification shown");
SnackBar snackBar = SnackBar(
backgroundColor: Colors.blueAccent,
content: Text(
body,
overflow: TextOverflow.ellipsis,
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.w600,
),
),
action: SnackBarAction(
label: "Go",
textColor: Colors.black,
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) {
return ActivityFeed();
}),
);
}),
);
_scaffoldKey.currentState.showSnackBar(snackBar);
}
//Notifications not shown.");
},
);
}
I tried different ways like getting the androidNotificationToken by updating in cloud firestore and getting it, but it didn't work.
[Users in Cloud Firestore][1]
[1] https://imgur.com/a/u5Df0zD
I'm just a beginner, trying to learn new stuff. Please do help.
Thank you,
SLN
Cannot read type error of undefined
error happens when the data dosent exist at docu.data().androidNotificationToken
Ensure the reference is correct, I prefer using this type of path for more clarity
let ref = db.collection(‘users’).doc(userID);etc
You can also catch a empty snapshot
var snapshot = await ref.get();
if(snapshot.empty){
console.log(‘snapshot is empty’);
}
I have use Cloud function connect with onesignal service which will send notification to user. After test function locally, it's work perfectly but after deploying to the cloud function it return me an error "RequestError: Error: read ECONNRESET" which I thick the cloud function reset the connection
Here is what my code look like
exports.sendNotification = functions
.pubsub
.topic('cron-notification')
.onPublish(async (message) => {
const databaseRef = admin.database();
// Query all user from realtime db
const snapshotsUser = await databaseRef
.ref(`user`)
.orderByKey()
.once("value");
// Check if user exist
if (snapshotsUser) {
//Get the user key
const user_object_key = Object.keys(snapshotsUser.val());
// send notification for each user
user_object_key.map(async (user_id) => {
// query something
const snapshotsUser = await databaseRef
.ref(`record/user_id`)
.orderByKey()
.once("value");
const message = {
"app_id": "xxxxxx-xxxx-xxxx-xxxx-xxxxxxxx",
"filters": [
{"field": "tag", "key": "user_id", "value": user_id}
],
"headings": {"en": `Hello World`},
"contents": {"en": `Hello`}
}
sendNotification(message);
})
}
});
function sendNotification(message) {
const headers = {
"Content-Type": "application/json; charset=utf-8",
"Authorization": "Basic XXXXXXXXXXXXXXXXXXXXXXXXXXX"
};
// Use onesignal for send notification
const options = {
uri: "https://onesignal.com/api/v1/notifications",
headers: headers,
method: 'POST',
json: true,
body: message,
resolveWithFullResponse: true,
}
return request(options).then(response => {
if (response.statusCode >= 400) {
throw new Error(`HTTP Error: ${response.statusCode}`);
} else {
console.log(response.body)
}
}).catch(error => {
console.log(error);
})
}
Can anyone give suggestion for me?
According to the Node.js documentation ‘ECONNRESET’ error occurs when a connection is forcibly closed by peer. This results in loss of connection on the remote socket due to timeout or reboot. Since you mentioned the code is working locally and the error occurs after it is deployed, here is an answer that says the possible solution is to increase the number of cores so the requests can be serviced faster. Also, it might be useful to read the GitHub discussion on socket hang up error.
Just for people who face the same problem with me, I just notice that I did not return anything in the function. The cloud function will reset the function if it did not return any thing back. So from the code above should be like this and everything work fine.
exports.sendNotification = functions.pubsub.topic('cron-notification').onPublish(async (message) => {
const databaseRef = admin.database();
// Query all user from realtime db
const snapshotsUser = await databaseRef
.ref(`user`)
.orderByKey()
.once("value");
// Check if user exist
if (snapshotsUser) {
//Get the user key
const user_object_key = Object.keys(snapshotsUser.val());
// send notification for each user
return Promise.all([ user_object_key.map(async (user_id) => {
// query something
const snapshotsUser = await databaseRef
.ref(`record/user_id`)
.orderByKey()
.once("value");
const message = {
"app_id": "xxxxxx-xxxx-xxxx-xxxx-xxxxxxxx",
"filters": [
{"field": "tag", "key": "user_id", "value": user_id}
],
"headings": {"en": `Hello World`},
"contents": {"en": `Hello`}
}
sendNotification(message);
})])}});
function sendNotification(message) {
const headers = {
"Content-Type": "application/json; charset=utf-8",
"Authorization": "Basic XXXXXXXXXXXXXXXXXXXXXXXXXXX"
};
// Use onesignal for send notification
const options = {
uri: "https://onesignal.com/api/v1/notifications",
headers: headers,
method: 'POST',
json: true,
body: message,
resolveWithFullResponse: true,
}
return request(options).then(response => {
if (response.statusCode >= 400) {
throw new Error(`HTTP Error: ${response.statusCode}`);
} else {
console.log(response.body)
}
}).catch(error => {
console.log(error);
})}