How to send data via HTTP request to Firestore database - node.js

So, I am trying to make an app with Node.js, Vue, and Firebase. I currently have a very basic function using firebase functions to send data to the realtime database via http request. I cant figure out how to do the same for Firestore database.
My goal is to send data or a file of some JSON data to my Firestore database from outside the app with an HTTP request. I know how to deploy functions I just am not sure how to send files or JSON to the database for further use.
Any help is greatly appreciated!
I have been playing around with different functions but can't seem to figure it out. I have looked at many of the docs/videos for firebase but I am pretty knew to this and can't quite grasp it.
const functions = require('firebase-functions');
const admin = require('firebase-admin')
admin.initializeApp() //need admin sdk
const toUpperCase = (string) => string.toUpperCase();
exports.addMessage = functions.https.onRequest((request, response) => {
const text = request.query.text;
const secretText = toUpperCase(text);
admin
.database()
.ref('/sentMSG')
.push({ text: secretText })
.then(() =>
response.json({
message: 'great!!!!',
text
})
)
.catch(() => {
response.json({
message: 'not great :^('
});
});
});
exports.writeToStore = functions.firestore.https.onRequest((request,
response) => {
let data = request.query.text
let setDoc = db.collection('cities').doc('LA').set(data)
.then(() =>
response.json({
message: 'great!!!!',
text
})
)
.catch(() => {
response.json({
message: 'not great :^('
});
});
});
The addMessage function adds data to realtime database but I need to do it for Firestore

index.js
const functions = require('firebase-functions');
const admin = require('firebase-admin');
const _ = require('lodash');
admin.initializeApp();
const db = admin.firestore();
const express = require('express');
const cors = require('cors');
const app = express();
const bodyParser = require('body-parser');
app.use(bodyParser.json());
app.get('/', async (req, res) => {
let data = req.query.data;
try {
await db.collection('users').doc().set({ userId: data });
} catch(err) { res.send(JSON.stringify(err)) }
res.send('Success');
})
app.post('/', async (req, res) => {
let payload = req.body;
let keys = Object.keys(payload);
let obj = {};
let i = 0;
try {
_.forEach(payload, async data => {
obj[keys[i]] = data;
i++;
})
await db.collection('users').doc().set(obj);
} catch(err) { res.send(JSON.stringify(err))}
res.send('Success');
})
exports.writeToFirestore = functions.https.onRequest(app);
You can then call this Cloud Function like such: linkToURL/writeToFirestore?data=5 or make a Post request to linkURL/writeToFirestore
From what I understand, whenever the Cloud Function is triggered, you want to write data in both the Firebase Realtime Database and in Firestore. Cloud Function will only execute one function in the exports object. In this case, since the function is being triggered by HTTPS, you can create a single exports.writeToBoth that encapsulates the code to write in Realtime DB and Firestore. Furthermore, I noticed that the writeToFirestore written is invalid, you need to change functions.firestore.https.onRequest to functions.https.onRequest.
In any case, you can refer to this code as a base, should you want:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
const db = admin.firestore();
let writeToFirestore = data => {
let setDoc = db.collection('users').doc('0').set(data);
}
let writeToRealtimeDB = data => {
// ...
// Your code
// ...
}
exports.writeToBoth = functions.https.onRequest((req, res) => {
let data = req.query.text;
// Write to both DB
try {
writeToFirestore(data);
writeToRealtimeDB(data);
} catch(err) { res.send('Error') }
res.send('Success');
})

Related

Data not saved into Firestore through scheduler function with Firebase Cloud Functions

I'm trying to create APIs with Firebase Cloud Functions so as to build a system to make what's collected through the APIs and other third-party APIs routinely saved into Firestore. Here is my source code index.js at this moment, and I'd like to control all the processes in this one file.
/functions/index.js
const firebaseAdmin = require("firebase-admin");
const firebaseFunctions = require("firebase-functions");
firebaseAdmin.initializeApp();
const fireStore = firebaseAdmin.firestore();
const express = require("express");
const axios = require("axios");
const cors = require("cors");
const serviceToken = "SERVICE-TOKEN";
const serviceBaseUrl = "https://api.service.com/";
const app = express();
app.use(cors());
const getAllExamples = async () => {
var url = `${serviceBaseUrl}/examples?token=${serviceToken}`;
var config = {
method: "get",
url: url,
headers: {}
};
axios(config).then((res) => {
console.log("Data saved!");
return res.data;
}).catch((err) => {
console.log("Data not saved: ", err);
return err;
});
}
const setExample = async (documentId, dataObject) => {
fireStore.collection("examples").doc(documentId).set(dataObject).then(() => {
console.log("Document written!");
}).catch((err) => {
console.log("Document not written: ", err);
});
}
app.get("/getAllExamples", (req, res) => {
getAllExamples().then((response) => res.send(response));
});
app.put("/setExample", (req, res) => {
setExample(req.params.documentId).then((response) => res.send(response));
});
const api = firebaseFunctions.https.onRequest(app);
module.exports = { api };
module.exports.updateExamplesRoutinely = firebaseFunctions.pubsub.schedule("0 0 * * *").timeZone("America/Los_Angeles").onRun((context) => {
var examples = getAllExamples();
for(var i = 0; i < examples.length; i++) {
var example = examples[i];
var exampleId = example["id"];
if(exampleId && example) setExample(exampleId, example);
}
});
As a result, updateExamplesRoutinely is properly triggered every 00:00 in PST, but NO data is stored in Firebase and NO console logs about Firebase data updates and found in Cloud Functions Logs.
Output data collected through https://api.service.com/ is something like this below:
[
{
id: "BB345",
name: "Tim Gordon",
age: 24,
email: "tgordon#yahoo.com"
},
{
id: "CC098",
name: "John Doe",
age: 28,
email: "john.doe#gmail.com"
},
{
id: "DD777",
name: "Adam Smith",
age: 39,
email: "adamsmith#outlook.com"
},
...
]
Simply, these 3 problems are what I would like to get resolved.
How should I call APIs defined in index.js through Cloud Functions' scheduler?
How should I save data into Firestore inside Cloud Functions' scheduler?
What's the best "async" way to call the third-party APIs, wait and collect the results, and pass them to other events or functions?
How should I call APIs defined in index.js through Cloud Functions'
scheduler?
I guess you want to say "How should I call the methods defined in index.js". There is no reason to call the APIs exposed by the Cloud Funtion from this same Cloud Function.
So to call the methods, do as follows:
const getAllExamples = async () => {
var url = `${serviceBaseUrl}/examples?token=${serviceToken}`;
var config = {
method: "get",
url: url,
headers: {}
};
return axios(config).then((res) => { // !!! note the return here
console.log("Data saved!");
return res.data;
}).catch((err) => {
console.log("Data not saved: ", err);
return err;
});
}
const setExample = async (documentId, dataObject) => {
// !!! note the return on next line
return fireStore.collection("examples").doc(documentId).set(dataObject).then(() => {
console.log("Document written!");
}).catch((err) => {
console.log("Document not written: ", err);
});
}
module.exports.updateExamplesRoutinely = firebaseFunctions.pubsub.schedule("0 0 * * *").timeZone("America/Los_Angeles").onRun(async (context) => {
const examples = await getAllExamples();
const promises = [];
for(var i = 0; i < examples.length; i++) {
var example = examples[i];
var exampleId = example["id"];
if(exampleId && example) promises.push(setExample(exampleId, example));
}
return Promise.all(promises);
// See how we return the promise returned by Promise.all
// !!! This is IMPORTANT, see https://firebase.google.com/docs/functions/terminate-functions
});
How should I save data into Firestore inside Cloud Functions'
scheduler?
The above code will save the data in Firestore. You actually want to execute a variable number of call to the setExample() method, you need to use Promise.all() as shown above. This way, setExample() will write to Firestore for each example.
What's the best "async" way to call the third-party APIs, wait and
collect the results, and pass them to other events or functions?
See how we adapt the updateExamplesRoutinely Cloud Function: we use the async and await keywords.
module.exports.updateExamplesRoutinely = firebaseFunctions.pubsub.schedule("0 0 * * *").timeZone("America/Los_Angeles").onRun(async (context) => {
const examples = await getAllExamples();
// ...

Use collection get from req.params - node.js

I'm using node.js and mongodb I need to know how to request the reading of a certain collection via API
(day_07-07-2021 is the collection to search)
in the Node.js route I wanted to query the collection day07072021, I wanted to know how I change the collection (on the fly) to query the route using the "req.param" received via URL.
so after setting the collection I'll do find();
Each day will have a collection.
http://localhost:3000/registry/windows2021/1101/day07072021
My actual code is (updated after: Selva Mary), but still not working.
logs.js
const express = require('express')
const router = express.Router()
const logs = require('../models/registry')
router.get('/:device/:rule/:bydate', async (req, res) => {
try {
let byDate = req.params.bydate +"info"
const logues = await db.collection(byDate).find({
'agent.name': req.params.device,
'rule.id': req.params.rule
}).sort({ 'id': -1 }).limit(15);
res.json(logues)
}
catch (err) {
res.status(500).json({ message: err.message })
}
})
server.js
require('dotenv').config()
const express = require('express')
const app = express()
const mongoose = require('mongoose')
const log = mongoose.set('debug', true)
mongoose.connect('mongodb://db:27017/datainfo', { useNewUrlParser: true,useUnifiedTopology: true })
const db = mongoose.connection
const registryRouter = require('./routes/registry')
app.use('/registry', registryRouter)
I hope its clear.
I get the message:
{"message":"db is not defined"}
Try this
router.get('/:device/:rule/:bydate', async (req, res) => {
try {
let byDate = req.params.bydate +"" // this makes the bydate as string. Collection name need to be in string
const logues = await logs.collection(byDate).find({
'agent.name': req.params.device,
'rule.id': req.params.rule
}).sort({ 'id': -1 }).limit(15);
res.json(logues)
}
catch (err) {
res.status(500).json({ message: err.message })
}
})

Firebase function works when deployed but not locally

I have a function that just fetches the data from my firebase and displays it. This works perfectly when deployed, but not locally.
I've attached my code just in case, but seeing as it works when deployed, I dont think that will be the problem, also its copy pasted from freecodecamp tutorial.
Directory is as follows:
firebase folder
|functions
||APIs
|||todos.js
||util
|||admin.js
||index.js
Also, the local version does have an output, its just the empty array initialised in todos.js line 9.
//todos.js
const { db } = require('complete file path');
exports.getAllTodos = (request, response) => {
db
.collection('todos')
.orderBy('createdAt', 'desc')
.get()
.then((data) => {
let todos = [];
data.forEach((doc) => {
todos.push({
todoId: doc.id,
title: doc.data().title,
body: doc.data().body,
createdAt: doc.data().createdAt,
});
});
return response.json(todos);
})
.catch((err) => {
console.error(err);
return response.status(500).json({ error: err.code});
});
};
//admin.js
const admin = require('firebase-admin');
admin.initializeApp();
const db = admin.firestore();
module.exports = { admin, db };
//index.js
const functions = require('firebase-functions');
const app = require('express')();
const {
getAllTodos
} = require('./APIs/todos')
app.get('/todos', getAllTodos);
exports.api = functions.https.onRequest(app);
I also performed export GOOGLE_APPLICATION_CREDENTIALS="path/to/key.json" to no avail.
You initialized the the app without any credentials:
const refreshToken; // Get refresh token from OAuth2 flow
admin.initializeApp({
credential: admin.credential.refreshToken(refreshToken),
databaseURL: 'https://<DATABASE_NAME>.firebaseio.com'
});
[Reference this site for more information:] https://firebase.google.com/docs/admin/setup/#initialize-without-parameters

Chaining GET requests to WP REST API in Express

I am struggling to understand callbacks, promises, and async/await.
What I want to do is read a .csv file inside my project folder that contains 150+ post ID's.
For each one of those ID's I want to make a https GET request to fetch a JSON response from my Wordpress website.
Then for each one of those posts that gets returned I want to insert them in my Firestore database.
I'm struggling with how to properly set up the callback functions.
Please help.
Thanks in advance.
const express = require('express');
const router = express.Router();
const https = require("https");
const Recipe = require("../includes/newrecipe");
var admin = require('firebase-admin');
var serviceAccount = require("../service_key.json");
const collectionKey = "recipes"; //name of the collection
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL: "<MY_FIRESTORE_URL>"
});
const firestore = admin.firestore();
const fs = require('fs');
const parse = require('csv-parser');
function prepareCsvData() {
return new Promise((resolve, reject) => {
//establish empty csvData array and filename to be referenced
var csvData = [];
var filename = 'wprm_recipe_ids.csv';
//read the csv file and push the data object into the array
fs.createReadStream(filename)
.pipe(parse(['ID']))
.on('data', (data) => csvData.push(data))
.on('end', () => { resolve(csvData); });
});
}
function getRecipeFromBlog(recipeId) {
return new Promise((resolve, reject) => {
//make the get request to my website to get the recipe
https.get('<MY_WEBSITE_URL>' + recipeId, (response) => {
var body = "";
response.on('data', function (chunk) { body += chunk; });
response.on('end', () => {
var { recipe } = JSON.parse(body);
//build new recipe to be exported
var newRecipe = new Recipe(recipe);
resolve(newRecipe);
});
});
});
}
/* GET recipes. */
router.get('/', async (req, res, next) => {
//first prepare the csv data
//function returns a promise with the csv data
//that I can then use in the next step
const csvData = await prepareCsvData();
for (var i = 0; csvData.length < i; i++) {
getRecipeFromBlog(csvData[i].ID)
.then((newRecipe) => {
//when I have a recipe for a given recipe ID
//update database in firestore
firestore
.collection(collectionKey)
.doc(""+newRecipe.id)
.set(newRecipe)
.then(function() {
console.log('document written');
});
});
}
res.send('done');
});
You need to do something like below:
Play around this, You'll get it working hopefully!
Let me know if that worked!
router.get("/", async (req, res, next) => {
const csvData = await prepareCsvData();
const recipePromises = [];
// check if data is empty or not
if (!csvData.length) {
return res.send("Empty data");
}
csvData.forEach((element) => {
recipePromises.push(getRecipeFromBlog(element.id));
});
// await for all promises parallelly.
const result = await Promise.all(recipePromises);
// Get a new write batch
const batch = db.batch();
result.forEach((recipe) => {
const ref = db.collection("recipes").doc(`${recipe.id}`);
batch.set(ref, recipe);
});
// Commit the batch
await batch.commit();
res.send("done");
});
The OP code looks pretty close to working. Have the promise-returning functions been tested? Assuming they work, first decorate them as async...
async function prepareCsvData() {...}
async function getRecipeFromBlog(recipeId) {...}
Create another promise-returning function to insert many recipes into firebase...
async function writeRecipesToFB(recipes) {
const collectionRef = collection(collectionKey);
const promises = recipes.map(recipe => {
return collectionRef.doc(`${recipe.id}`).set(recipe);
});
return Promise.all(promises)
}
As another answer suggests, as an alternative, firebase's batch write is a good idea...
async function writeRecipesToFB(recipes) {
// as a set of promises
const collectionRef = collection(collectionKey);
const batch = db.batch();
recipes.forEach(recipe => {
const docRef = collectionRef.doc(`${recipe.id}`)
batch.set(docRef, recipe)
});
return batch.commit();
}
Now the express function is easy to write...
router.get('/', async (req, res, next) => {
const csvData = await prepareCsvData();
let promises = csvData.map(row => {
return getRecipeFromBlog(row.ID);
});
const recipes = await Promise.all(promises);
await writeRecipesToFB(recipes);
res.send('done');
});

Cloud Functions for Firebase Notification

I am trying to fire a notification using Cloud Functions for Firebase. I can get it to console log stating a message has been fired, but can't actually get the notification to work in the browser. Can anyone see a flaw?
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
exports.newMessageAlert = functions.database.ref('/messages/{message}').onWrite((event) => {
const message = event.data.val();
const getTokens = admin.database().ref('users').once('value').then((snapshot) => {
const tokens = [];
snapshot.forEach((user) => {
const token = user.child('token').val();
if (token) tokens.push(token);
});
return tokens;
});
const getAuthor = admin.auth().getUser(message.uid);
Promise.all([getTokens, getAuthor]).then(([tokens, author]) => {
const payload = {
notification: {
title: `Hot Take from ${author.displayName}`,
body: message.content,
icon: author.photoURL
}
};
admin.messaging().sendToDevice(tokens, payload).then((resp) =>{
console.log("IT WORKED", resp);
});
});
});

Resources