writing to cloud firestore via cloud functions not working - node.js

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');
})

Related

Trouble Deleting Object in Mongo Collection (Express / Monk)

I am trying and failing to delete an object from a mongo collection that I created. I can make get and post requests just fine, but I am totally lost as to how to delete an item from a given collection. I am currently just deleting the entire collection when trying to pass in the item by id...
Just so you can see what I am working with...
/**** IMPORTS *****/
const express = require('express');
const cors = require('cors');
const monk = require('monk');
const app = express();
const db = monk('localhost/att');
// Mongo Collection packs holds package objects
const packs = db.get('packages');
app.use(cors());
app.use(express.json());
And my back-end delete path:
//packs is my collection
app.delete('/packages/:id', (req, res) => {
packs.remove({"_id": req.body.id});
});
and my function on the front-end:
function listAllPackages() {
fetch(API_URL_PACKAGES).then(response => response.json()).then(packageObjs => {
// Access to all the package objects in DB here
// packageObjs : Array of Packages
let html = '';
packageObjs.forEach(p => {
html += `<option id="${p._id}" name="${p._id}">${p._id} - Name: ${p.name}, Price: ($)${p.price}, Duration: ${p.duration}(minutes)</option>`;
});
allPackages.innerHTML = html;
const delPackageDiv = document.querySelector('.deletePackage');
delPackageDiv.innerHTML = `<button id='deletePackageSubmit'>DELETE PACKAGE</button>`;
const delPackageButton = document.querySelector('#deletePackageSubmit');
delPackageButton.addEventListener('click', () => {
// GET SELECTED PACKAGE
const delForm = document.querySelector('.showPackages');
const opt = delForm.options[delForm.selectedIndex];
fetch(API_URL_PACKAGES + '/' + opt.id, {
method: 'DELETE'
});
});
});
}
Figured it out finally! Here's my solution in case anyone else is struggling like I was...
Change this:
//packs is my collection
app.delete('/packages/:id', (req, res) => {
packs.remove({"_id": req.body.id});
});
To this:
//packs is my collection
app.delete('/packages/:id', (req, res) => {
packs.remove({"_id": req.params.id });
});
Voila!

How to use Firebase admin SDK with google cloud functions

I'm trying to generate an email sign in link using the admin.auth().generateSignInWithEmailLink method provided by the Firebase Admin SDK. I'm attempting to do this within a Google cloud function.
Here is my index.js file (All packaged are installed):
const cors = require('cors')({ origin: true });
const functions = require('firebase-functions');
const admin = require('firebase-admin');
exports.sendLoginLink = function(req, res) {
cors(req, res, () => {
const actionCodeSettings = {
url: 'http://localhost:8083/account/dashboard/?email=' + req.body.email,
handleCodeInApp: true
};
admin.auth().generateSignInWithEmailLink(req.body.email, actionCodeSettings)
.then((link) => {
console.log(link)
})
.catch((error) => {
res.status(500)
});
});
};
I've passed an email from my front-end app to the cloud function which works, the actionCodeSettings also output correctly, but i'm not able to make it past the first line in the generateSignInWithEmailLink() when debugging.
Anyone know why?

How to save external API response to Firebase

Im working with a React App where I present a list top Podcasts. I'm using iTunes Search API to dynamically present data to the user. For now, I working with a Node Express server to setup my custom endpoints. The problem is that the API has a request limit, so I tought that I could save what I get from the response to Firebase and present the data from firebase instead.
To my question;
Can in some way save the response I get from iTunes Search API to Firebase?
For now my code for fetching data from my API Endpoints looks like this in my Node+Express server:
const express = require('express');
const unirest = require('unirest');
const app = express();
const port = process.env.PORT || 5000;
// Get all Episodes from a specific podcast
app.get('/api/podcast/episodes', (req, res) => {
const feedurl = req.query.feedurl
unirest.get(feedurl)
.end((response) => {
res.status(200).send(response.body)
});
});
// Get Podcast by ID
app.get('/api/podcast/:id', (req, res) => {
const podID = req.params.id;
unirest.get(`https://itunes.apple.com/lookup?id=${podID}&country=se`)
.end((response) => {
res.status(200).send(response.body)
});
});
// Get Podcast Categorys
app.get('/api/podcast/:category/:amount', (req, res) => {
const categoryID = req.params.category;
const amount = req.params.amount;
unirest.get(`https://itunes.apple.com/se/rss/toppodcasts/limit=${amount}/genre=${categoryID}/explicit=true/json`)
.end((response) => {
res.status(200).send(response.body)
});
});
// Get Podcast Categorys
app.get('/api/categorys', (req, res) => {
unirest.get('https://itunes.apple.com/WebObjects/MZStoreServices.woa/ws/genres?id=26&cc=se')
.end((response) => {
res.status(200).send(response.body)
});
});
app.listen(port, () => console.log(`Listening on port ${port}`));
Im just looking for someone who could point me in the right direction how to proceed. Cause for now I'm stuck, big time.
Depending on how long you want to cache the response, you can use a whole different things - a physical database like MySql, Sqlite, MongoDB etc to locally persist data.
If you only want to keep the cached result for a short period of time, you can use in-memory cache or just any other tool that offers you same functionality. Redis is also a good contender as a temporary store, especially when you expect to scale to more than one node instance for your application.
Below, I have modified a part of your code to cache result for 10mins, using memory-cache npm module
const express = require('express');
const unirest = require('unirest');
const cache = require('memory-cache');
const CACHE_DURATION = 10 * 60 * 1000; //10mins
const app = express();
const port = process.env.PORT || 5000;
// Get all Episodes from a specific podcast
app.get('/api/podcast/episodes', (req, res) => {
const cacheKey = req.query.feedurl; //Or anything unique to this route
const cachedData = cache.get(cacheKey);
if(cachedData) {
return res.json(cachedData);
}
const feedurl = req.query.feedurl
unirest.get(feedurl)
.end((response) => {
res.status(200).send(response.body);
cache.put(cacheKey, response.body, CACHE_DURATION);
});
});
---- the rest of your code ----
You can hit the route as many times as you want and be guaranteed that data will be fetched from iTunes only once in 10mins.
The second and subsequent requests will be served a lot faster from cache.
Let me know if this is what you are looking for.

Proper return for service functions in nodejs web applications to respond to client synchronously

I've posted an approximation of my node web application below. The original problem I had is that I want to be able to let the client know on post to createentity whether the insert was successful and the id of the inserted entity. However, connection.query having a callback rather than running synchronously, I can't use the entityservice how I'd expect in another language, ie simply returning the result synchronously. There are several solutions, and I'm curious which is the best/common practice re node.js or if there is another I'm not thinking of.
Passing res down to the service, and responding within a callback; seems poor practice
Similarly, passing functions to execute after success/failure to the service; also seems poor practice
Returning a promise from the service and setting res based on resolution/failure; seems like services shouldn't return promises but I'm new to node
Some better method using appropriate features of node.js of which I'm unaware
trying to change the service such that it runs synchronously and just returning the result Other questions/answers have made me leery that this is possible or wise
structure the application some other way
something else?
//app.js
var express = require('express');
var bodyParser = require('body-parser')
var path = require('path');
var EntityService = require('./entityService.js');
var app = express();
var urlencodedParser = bodyParser.urlencoded({ extended: true })
app.post('/createentity', urlencodedParser, function(req, res){
EntityService.createEntity(req.body.name);
res.status(200).json(null);
});
app.listen(3000);
//entityService.js
var mysql = require('mysql');
EntityService = function(){
var connection = mysql.createConnection({
host : CONNECTION_IP,
user : 'root',
password : 'password',
database : 'entitydb'
});
connection.connect();
this.createEntity = function(name){
var record = {name: 'name'};
var query = connection.query('INSERT INTO entity set ?', record, function(error, results, fields ){
//want to return the results.insertId from the service
});
}
}
module.exports = new EntityService();
The correct approach here is option 3 - have your service return a Promise
this.createEntity = name => new Promise((resolve, reject) => {
const query = connection.query('...', { name }, (err, results) => {
if (err) return reject(err);
return resolve(results.map(r => r.insertId));
});
})
If you're on the latest version of Node, I'd go with the asynch/await syntax.
Your service returns a promise,then your calling code can do:
app.post('/createentity', urlencodedParser, async function(req, res){
const entity = await EntityService.createEntity(req.body.name);
res.status(200).json(entity);
});

Firebase Cloud Functions Simply Refuse to Read From Realtime Database

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.

Resources