Firebase Cloud Functions Simply Refuse to Read From Realtime Database - node.js

Here is my code:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
exports.createNewGame = functions.https.onRequest((request, response) => {
var database = admin.database();
database.ref().on('value', function(data) {
console.log('SOMETHING');
});
response.end();
});
The problem is, it is not logging "SOMETHING". It is as if it is refusing to call the "on" function. It logs that the function was called and that it was executed successfully, however, it does not actually run anything inside the callback function in "on".
Any help is appreciated, I have no idea what else to try to get this to run, I have literally copy-pasted code from Firebase's documentation to no avail.
Cheers!

You're sending the response before the database read from the database. This likely terminates the function before the log statement can execute.
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
exports.createNewGame = functions.https.onRequest((request, response) => {
var database = admin.database();
database.ref().once('value', function(data) {
console.log('SOMETHING');
response.end();
});
});
Note that I also changed on() to once(), since you're only interested in receiving the current value.

Related

Removing Firebase Realtime Database node using Cloud Function

I can successfully get a query param and post it into my realtime database at a path defined by the query param itself, using this code:
'use strict';
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
exports.emptyHat = functions.https.onRequest(async (req, res) => {
// Grab the group parameter.
const group = req.query.group;
// Push the new message into the Realtime Database using the Firebase Admin SDK.
const snapshot = await admin.database().ref('/group').push({list: group});
// Redirect with 303 SEE OTHER to the URL of the pushed object in the Firebase console.
res.redirect(303, snapshot.ref.toString());
});
If the query param was 'test', the result would be a new entry at /test/{firebaseID}/{'list':'test'}
When I tried to modify it to remove the node named in the query parameter I get errors.
(I'm trying to remove that top level /test node
'use strict';
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
exports.emptyHat = functions.https.onRequest(async (req, res) => {
// Grab the group parameter.
const group = req.query.group;
// Remove the node at the location 'group'.
functions.database().ref('/group').remove();
// Redirect with 303 SEE OTHER to the URL of the pushed object in the Firebase console.
//res.redirect(303, snapshot.ref.toString());
});
Error message in the logs:
at exports.emptyHat.functions.https.onRequest (/srv/index.js:95:13)
at cloudFunction (/srv/node_modules/firebase-functions/lib/providers/https.js:49:9)
at /worker/worker.js:783:7
at /worker/worker.js:766:11
at _combinedTickCallback (internal/process/next_tick.js:132:7)
at process._tickDomainCallback (internal/process/next_tick.js:219:9)
Your second function is ignoring the promise returned by the Firebase API. This makes it unlikely to work in Cloud Functions. Cloud Functions HTTP triggers require that you only send a response only after all the asynchronous work is complete. After your function generates a response, it will terminate and clean up any async work still in progress. That work might not complete.
Your second function should be more like your first one, and use the promise returned by remove() and wait for it to complete before sending the response:
exports.emptyHat = functions.https.onRequest(async (req, res) => {
const group = req.query.group;
await admin.database().ref('/group').remove();
// send the response here.
});

writing to cloud firestore via cloud functions not working

I am trying to write data into cloud firestore from cloud functions, and I am not getting it to work. Here is my code in cloud functions:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
var express = require('express');
admin.initializeApp(functions.config().firebase);
const app = express();
let db = admin.firestore();
app.get('/helloworld', (req, res) => res.send('Hello World!'));
app.post('/signup', (req, res) => {
var email = req.body.email;
var username = req.body.username;
var password = req.body.password;
//creating document. Here is where it isn't working
let docRef = db.collection('UsersMain').doc('firstdoc');
let data = {
Email: 'a#gmail.com',
UserName: 'Matt'
};
let setDoc = docRef.set(data);
console.log(db);
res.send('Login Complete');
});
const api1 = functions.https.onRequest(app);
module.exports = {api1};
and here are my firestore permissions:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read, write: if true;
}
}
}
Can someone please take a look and let me know what I'm doing wrong? Ideally, a new document called 'firstdoc' would be created and it would have the Email and UserName data in it. Thanks!
The first thing that pops to mind looking at your code is that you're not waiting for docRef.set(data) to complete before sending a result back to the caller. This means that Cloud Functions may kill your environment, before the write to Firestore is completely.
To ensure the write is completed, you should only write a result to the response once the write to the database is completed. Something like:
docRef.set(data).then(() => {
res.send('Login Complete');
})

agent.add() sometimes not able to give results in dialogflow

I am using Dialogflow to build a chatbot and I am using Axios to make some posts and get requests, but sometimes agent.add() inside that HTTP call doesn't work.
I am attaching the sample code.
const functions = require('firebase-functions');
const {WebhookClient} = require('dialogflow-fulfillment');
const {Card, Suggestion} = require('dialogflow-fulfillment');
// Using some external libraries
//axios for using third party api hosted as http.
const axios = require('axios');
// xml2js for parsing xml output in response
const xml2js = require('xml2js');
const json = require('json');
// Accessing firebase admin
const admin = require('firebase-admin');
function name(agent){
return axios.post('http://some-api,
{"name": "Kyle"}).then((result) => {
console.log('Store datato api');
agent.add('What is your email id'); // Sometimes its working and sometimes its not
//return Promise.resolve(agent); // I tried this as well but the same issue.
});
}
What could be appropiate changes, as I was going through all other questions on Stackoverflow, I tried returning pomise as well, but didn't work.
It looks like you've structured your Promise incorrectly. This is the format I use to return responses that require Promises:
function name(agent) {
const promise = new Promise(resolve => {
// logic goes here
resolve(agent.add(output));
});
return promise;
}
If this doesn't work, check other points it may be failing at - are you sure your webhook isn't failing in the POST request?

Aws lambda with mongoDb connection

Hello guyz i need an answer to an simple question.I am using Aws lambda with serverless framework and i am using mongoDb connection in aws lambda.I have used connection code inside my handler function and i have used connection pooling.
Now when i deploy my app on AWS using sls deploy and after deploying when i call my lambda for first time then connection is established only once and after that on other lambda API calls it is reusing my connection instead of creating new connection.so this thing is fine.
Now after this process i am running a script which is not related with my AWS app to test my concurrent lambda requests.I have called my same lambda API using request npm module in for loop in script and in that case all time my new connnection is created till loop terminates instead of using my existing one generated from first call.Can someone tell me why it is happening and what is the reason behind this? Why my connection is creating again when this script runs when already i have created my connection on first lambda call.
And same api when i call from postman then it is resuing my connection after first lambda call but when i run this script and from inside script i call this API(using request NPM module) using command "node app.js" then all time till loop terminates it creates new connection.
Please help me out in this.
'use strict'
const bodyParser = require('body-parser')
const express = require('express')
const serverless = require('serverless-http')
const cors = require('cors');
const mongoConnection = require('./connection/mongoDb');
const app = express()
app.use(cors())
app.use(bodyParser.json())
const handler = serverless(app);
let cachedDb = null;
module.exports.handler = async (event, context) => {
context.callbackWaitsForEmptyEventLoop = false;
if (cachedDb == null) {
let Database = await mongoConnection();
console.log("DB", Database);
cachedDb = Database
}
const baseRouter = require('./routes/index');
app.use('/api', baseRouter);
const result = await handler(event, context);
return result;
};
Here is a node.js example that shows the connection parameters. Perhaps this will help?
const express = require("express");
const bodyParser= require("body-parser")
const app = express();
const MongoClient = require("mongodb").MongoClient
MongoClient.connect("mongodb://myusername:mypassword#localhost:27017", (err, client) => {
if (err) return console.log(err)
var db = client.db("mydatabase")
db.collection("mycollection").countDocuments(getCountCallback);
app.listen(3000, () => {
console.log("listening on 3000")
})
})
function getCountCallback(err, data) {
console.log(data);
}
app.use(bodyParser.urlencoded({extended: true}))
app.get("/", (req, res) => {
res.sendFile(__dirname + "/index.html")
})
app.post("/quotes", (req, res) => {
console.log(req.body)
})
Your example code does not show any hostname for your database server, nor does it specify which port to use. Please compare your code and contrast to my example.
I see you defined the cachedDb variable outside the handler scope, so that makes it available when the container is reused. However, there is no guarantee that the container will be reused (see my previous link on that) because that's not how Lambda works. If you invoke the same functions many times very quickly after eachother, Lambda needs to scale out horizontally to be able to handle the requests quickly. They each get their own container and connection.
When the invocation is finished, AWS will keep the container for a bit (how long depends on many factors like function size & RAM limit). If you invoke it again the containers can reuse their connection. You can try to invoke the function 20 times with 1 second interval and counting the number of connections that have been openend. It will be lower than 20, but higher than 1.

Dialogflow Node.js Client - save data on data side, but don't return response

I'm using DialogFlow with the official Google Node.js library. I want to use a webhook to save input data to a database, but not return a response.
However, currently I'm just waiting for the function to timeout which is slow, and writes an error to the logs Error: No responses defined for platform: FACEBOOK
I've checked the documentation hoping for a way to send a 200 status or similar, but haven't found anything. https://dialogflow.com/docs/reference/fulfillment-library/webhook-client
Is it possible to do what I'd like to do? It seems like a fairly standard requirement.
UPDATE: My code is simple, but below
const functions = require('firebase-functions');
const {WebhookClient} = require('dialogflow-fulfillment');
process.env.DEBUG = 'dialogflow:debug'; // enables lib debugging statements
const firebase = require('firebase-admin');
firebase.initializeApp();
const session_split = agent.session.split('/');
const session = session_split[blf_session_split.length - 1];
exports.dialogflowFirebaseFulfillment = functions.https.onRequest((req, res) => {
const agent = new WebhookClient({
request: req,
response: res
});
agent.handleRequest(savedata);
function input(agent) {
return firebase.database().ref('/tests/' + session).update({
"input": agent.action
});
}
});

Resources