Firebase cloud functions emulator - how to connect node.js to emulator - node.js

I've installed firebase emulator to be able to develop some cloud function locally before deploy them.
Im reading this guide but I'm not sure how to proceed. The example use the import syntax but I need to write a node.js cloud function and I'm not sure how to proceed.
How I can correctly connect to the cloud functions emulator from my index.js file that will contain the needed logics?
At the moment my file looks like this:
const admin = require('firebase-admin')
const functions = require("firebase-functions");
const stripe = require('stripe')
admin.initializeApp()
// // Create and Deploy Your First Cloud Functions
// // https://firebase.google.com/docs/functions/write-firebase-functions
//
// exports.helloWorld = functions.https.onRequest((request, response) => {
// functions.logger.info("Hello logs!", {structuredData: true});
// response.send("Hello from Firebase!");
// });
const db = admin.getDatabase()
What I need is to use stripe but to achive my scope I need to read from firebase realtime database first and I didn't setup that emulator but only the cloud functions one.
Any suggestion will be appreciated, thank you!

Related

How to make my firestore listeners secure/security?

Hello I have a listener with firebase so that whenever there is a change that it will do a function. I need to put this listener in a software so that when it detects a change that it will perform a function. How would I be able to put this listener in my other code and make sure that my database stays secure and that nobody will be able to mess with it?
const functions = require("firebase-functions");
// // Create and Deploy Your First Cloud Functions
// // https://firebase.google.com/docs/functions/write-firebase-functions
const express = require('express');
const cors = require('cors');
const admin = require('firebase-admin');
admin.initializeApp();
const app = express();
const db = admin.firestore()
exports.onCreate = functions.firestore.document('users/{userId}').onCreate(async (snap, context) => {
const values = snap.data();
await db.collection('logging').add({description : `SKU was sent to discord with ${values.username}`})
})
Using Firebase functions and Firestore, you can do the following:
Keep all your Firebase functions server-side, no client-side anything. Server-side functions have admin access but they stay inaccessible to anyone except the firebase account owners/managers.
You then use an "onUpdate" firebase function: every time your document (data) changes, your Firebase function runs.
So what should it do?
Simply have it call your external (untrustworthy) software via an HTTP GET or POST call. You can do that using Axios, and even pass JSON or query parameters to that external software. You can have that external code do what it's supposed to do, without ever having to interact directly with your database.

Firebase test auth cloud functions locally

exports.sendWelcomeEmail = functions.auth.user().onCreate((user) => {
console.log(user.uid);
console.log(user.email);
console.log(user.displayName);
});
exports.getUserInfo = functions.https.onCall(async (data, context) => {
// get array of user IDs and returns information (from Users collection)
const userIDs = data.userIDs;
const result = [];
const querySnapData = await admin.firestore().collection("Users").get();
querySnapData.forEach((ele) => {
if (userIDs.indexOf(ele.id) !== -1 && ele.id !== context.auth.uid) {
result.push(ele.data());
}
});
return { res: result };
});
I've got these two functions in my project - one is callable function and the other one is auth trigger functions.
So in my client app, I run
firebase.functions().useFunctionsEmulator('http://localhost:5001');
let getUserInfo = functions.httpsCallable('getUserInfo');
getUserInfo({userIDs: data}).then(res => doSomething);
And to run the cloud functions locally
firebase emulators:start
But it says
functions[sendWelcomeEmail]: function ignored because the auth emulator does not exist or is not running.
So in the client App, getUserInfo works pretty well but can't trigger onCreate.
But I was not able to find any document about auth emulator.
Any link/article/video or answer is appreciated.
The Firebase Emulator Suite currently Cloud Firestore, Realtime Database, Cloud Functions, and Cloud Pub/Sub. It does not yet emulate Firebase Authentication APIs. So any auth calls you make will be executed against the real project that is associated with the emulators.
This also means that your functions.auth.user().onCreate((user) => { Cloud Function will not be triggered in the emulators at the moment. You'll have to deploy it to the servers to test this trigger.
To learn when an auth emulator is available, I recommend keeping an eye on Firebase's release notes, and on the main documentation page for the emulator suite that lists the supported products. You can also follow along more closely on Github, either in the commits, or in this feature request.
firebase recently released the Authentication Emulator, you can check it via the release notes here: https://firebase.google.com/support/releases#october_26_2020, and further guide here: https://firebase.google.com/docs/emulator-suite/connect_auth
So as #Franek van Puffelen wrote above, it is not done yet.
Was able to test auth functions locally like below.
function sendWelcomeEmail(user) {
console.log(user.uid);
console.log(user.email);
console.log(user.displayName);
}
exports.sendWelcomeEmail = functions.auth.user().onCreate((user) => sendWelcomeEmail(user));

How can I deploy a discord.js bot to Cloud Functions?

I want to deploy a Discord bot running on discord.js to Firebase Cloud Functions but I can't get the bot to run in Firebase. If I use nodemon it runs perfectly but if I use firebase deploy it will not start the bot.
Here is my current code:
const functions = require('firebase-functions');
require('dotenv').config();
const token = process.env.TOKEN
const Discord = require('discord.js')
const Client = new Discord.Client();
Client.on('ready', () => {
Client.channels.find(x => x.name === 'main-cannel').send('bot is deployed')
Client.user.setGame(`The Cult | ${Client.guilds.size} servers`)
Console.log('test')
});
Client.login(token);
//is is not working but de basic
//export.App = functions.... {Client}
exports.app = functions.https.onRequest((request, response) => {
response.send("Test");
});
This may not be the best combination of google cloud platform services, since cloud functions where not designed with this in mind. You can just host your Discord bot on a compute engine machine.
If you want to use the dynamic scaling have a look at Discord Microservice Bots where DasWolke describes what microservices are. Hey also included his javascript code to split up the different services for Discord.
What you can do on Google cloud platform specifically, is creating a VM with the Gateway running. This needs to run 24/7 and should be lightweight. You can use an f1-micro (which is free) for this though google recommends a g1-small for the task.
The gateway should filter the events you are looking for (because Discord sends a lot of events and you don't need most) and send the data to cloud function or cloud run (you can send the data via pub/sub). In my experience, a cloud run has much shorter startup times so I went with that.
Inside your function, you receive the data do with it what you want. If you want something in Discord (send a message, manage channels, ...) you can use SnowTransfer for that. SnowTransfer just calls the rest API on discord.
Put client.login(token) into where response.send("Test"); is. This will prompt your bot's code to execute upon an HTTP request.
In https://us-central1-<your project ID>.cloudfunctions.net/app, replace <your project ID> with your project's ID.
If Firebase Hosting gives you the URL example-123.web.app, your project ID is example-123. You can also get your project ID through the console: open the Firebase console, select your project, click the settings icon, go to "Project settings," and its ID is on the second line of the table.
For this to work, you have to move your token straight into the code. You can't use a shell file like .env.
In the end, your code will be:
const functions = require('firebase-functions');
const token = 'whatever the token is';
const Discord = require('discord.js')
const Client = new Discord.Client();
Client.on('ready', () => {
Client.channels.find(x => x.name === 'main-cannel').send('bot is deployed')
Client.user.setGame(`The Cult | ${Client.guilds.size} servers`)
Console.log('test')
});
exports.app = functions.https.onRequest((request, response) => {
response.send("Test"); // Do not delete this! Your request will time out if you do.
Client.login(token);
});
As Gabber235 noted, this is probably not the best Google Cloud Platform service to use for this, and you should probably use Compute Engine.

Why is Google Cloud Functions throwing a "Invalid value for config firebase.databaseURL" error when I try to initialize a firebase app?

I have a Google Cloud Function that syncs presence information from a Firebase realtime database to a Firestore database (as explained here). This is the relevant Cloud Functions code from the linked example:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
// Since this code will be running in the Cloud Functions enviornment
// we call initialize Firestore without any arguments because it
// detects authentication from the environment.
const firestore = admin.firestore();
// Create a new function which is triggered on changes to /status/{uid}
// Note: This is a Realtime Database trigger, *not* Cloud Firestore.
exports.onUserStatusChanged = functions.database.ref('/status/{uid}').onUpdate(
(change, context) => {
// Get the data written to Realtime Database
const eventStatus = change.after.val();
// Then use other event data to create a reference to the
// corresponding Firestore document.
const userStatusFirestoreRef = firestore.doc(`status/${context.params.uid}`);
// It is likely that the Realtime Database change that triggered
// this event has already been overwritten by a fast change in
// online / offline status, so we'll re-read the current data
// and compare the timestamps.
return change.after.ref.once('value').then((statusSnapshot) => {
const status = statusSnapshot.val();
console.log(status, eventStatus);
// If the current timestamp for this data is newer than
// the data that triggered this event, we exit this function.
if (status.last_changed > eventStatus.last_changed) {
return null;
}
// Otherwise, we convert the last_changed field to a Date
eventStatus.last_changed = new Date(eventStatus.last_changed);
// ... and write it to Firestore.
return userStatusFirestoreRef.set(eventStatus);
});
});
I recently received an email from Google informing me that I will need to update from NodeJS 6 to NodeJS 8 or 10. As this particular function isn't in production yet, I went ahead and made the configuration change in the Google Cloud Console. I now get the error below. I tried switching back to NodeJS 6, recreating the function from scratch, checking Github issues and other online forums. It appears that my Google Cloud Function is no longer being provided with the necessary environment variables to connect with Firebase/Firestore. However, I'm unsure why that would be the case.
Error: Invalid value for config firebase.databaseURL: undefined
at resourceGetter (/srv/node_modules/firebase-functions/lib/providers/database.js:101:19)
at cloudFunctionNewSignature (/srv/node_modules/firebase-functions/lib/cloud-functions.js:102:13)
at /worker/worker.js:825:24
at <anonymous> at process._tickDomainCallback (internal/process/next_tick.js:229:7)
This error also shows up in the Stackdriver logs for the Cloud Function:
Warning, estimating Firebase Config based on GCLOUD_PROJECT. Initializing firebase-admin may fail
You should redeploy using the Firebase CLI. It does some special things in the environment to help the Firebase Admin SDK initialize correctly without any parameters (adding FIREBASE_CONFIG). It sounds like when you changed the runtime in the console, you also lost this special configuration.
For me, I use firestore, and I was getting the same error as you, so I had to create a real-time database without any record then I set the credentials for the admin like so:
const functions = require("firebase-functions");
const admin = require("firebase-admin");
admin.initializeApp({
databaseURL: "your realtime database url"
});
When you are done, run firebase deploy --only functions to deploy your functions.
Here is your Realtime database URL:

Firebase functions: koa.js server how to deploy

I already have an app written in MERN stack with koa server prepared build version. My main node file to run by node server.js command to start the whole app looks like this.
In every tutorial, I see that I need to add functions.https.request etc. in the beginning of coding (or at least to suppose doing it).
How could I host my app on firebase the same as I could on heroku - with whole server side?
It is possible to host Koa app using firebase functions, I figure it out after some Googling and analyzing.
This is a piece of code from my project, it is now hosted with firebase functions:
const Koa = require('koa');
const app = new Koa();
// ... routes code here ...
// This is just for running Koa and testing on the local machine
const server = app.listen(config.port, () => {
console.log(`HITMers-server is running on port ${config.port}`);
});
module.exports = server;
// This export is for Firebase functions
exports.api = functions.https.onRequest(app.callback());
You can see the docs and tutorial video for more information.
By the way, here is another example to deploy Koa to now.sh version 2.
You can actually skip the listen call entirely, and use app.callback().
This seems to make more sense than listening on a random port that never actually gets hit.
const functions = require('firebase-functions');
const app = new Koa();
... // set up your koa app however you normally would
app.use(router.routes());
module.exports.api = functions.https.onRequest(app.callback());
You can run an express application using firebase hosting to serve dynamic content via firebase functions. You cannot, however, use Koa.js currently. The functions.https.onRequest requires you to pass an HTTP request handler or an express app returned from express().
Here is the relevant article from Firebase about serving dynamic content from functions.
https://firebase.google.com/docs/hosting/functions
Here is a video tutorial from Firebase on using express.
https://www.youtube.com/watch?v=LOeioOKUKI8
To anyone looking for koa Google Cloud Functions, Here is my working version in typescript
import Koa from 'koa';
import Router from 'koa-router';
import type { HttpFunction } from '#google-cloud/functions-framework/build/src/functions';
const app = new Koa();
const port = process.env.PORT || 3001;
const router = new Router();
router.get('/', async (ctx) => {
ctx.body = 'Hello World!';
});
app.use(router.routes());
// For development on local
if (!isCloudFunctions()) {
app.listen(port, () => {
console.log(`Server running on port ${port}`);
});
}
export const helloWorldApi: HttpFunction = app.callback();
function isCloudFunctions(){
return !!process.env.FUNCTION_SIGNATURE_TYPE;
}
For deployment:
gcloud functions deploy test-koa-function --entry-point=helloWorldApi --runtime nodejs16 --trigger-http --allow-unauthenticated
You can't deploy and run an arbitrary node app on Cloud Functions. You have to make use of the different types of triggers that are defined by the product.
See the Cloud Functions for Firebase main page to see the list.
Cloud Firestore Triggers
Realtime Database Triggers
Firebase Authentication Triggers
Google Analytics for Firebase Triggers
Crashlytics Triggers
Cloud Storage Triggers
Cloud Pub/Sub Triggers
HTTP Triggers

Resources