My application makes use of Firestore Function Triggers to perform background actions based on changes in the Firestore, e.g. user profile changes. For example, if they change their mobile number, a verification code is sent.
I have a trigger that should run when an onWrite() event happens on a specific collection. onWrite() runs when any of the following actions occur in Firebase on a specific collection:
onCreate()
onUpdate()
onDelete()
In my usecase, I need it to run for onCreate() and onUpdate(), thus I use onWrite()
For Firebase Triggers to work, a specific format is expected in addition to a document id/wildcard representing a document that was created/changed/deleted.
Constants:
const collections = {
...
conversations: "conversations",
...
}
Callable Function (updates firestore):
/**
* Add an conversation to conversations collection
* #type {HttpsFunction & Runnable<any>}
*/
exports.addConversations = functions.https.onCall(async (data, context) => {
// expects conversation & interested state
const {valid, errors} = validateRequiredBody(data, [
"conversation",
]);
if (!valid) {
return {
status: false,
message: "Missing or invalid parameters",
errors: errors,
jwtToken: "",
};
}
// get conversation item
const conversation = {
id: data["conversation"]["id"],
name: data["conversation"]["name"],
}
// create conversation with empty counter
// let writeResult = await collectionRefs.conversationsRef.doc(conversation.id).set({
let writeResult = await admin.firestore().collection(collections.conversations).doc(conversation.id).set({
id: conversation.id,
name: conversation.name,
count: 0
});
console.log(`[function-addConversations] New Conversation [${conversation.name}] added`);
return {
status: true,
message: ""
}
});
Firestore Trigger (not triggering):
/**
* On conversations updated/removed, update corresponding counter
* #type {CloudFunction<Change<QueryDocumentSnapshot>>}
*/
exports.onConversationProfileCollectionCreate = functions.firestore.document(`${collections.conversations}/{id}`)
.onWrite(async snapshot => {
console.log("Conversation Collection Changed");
// let conversation = collectionRefs.conversationsRef.doc(snapshot.id);
// await conversation.update({count: FieldValue.increment(1)});
});
In my mobile application, the user (calls) the addConversations() firebase function, this adds the new conversation to Firestore which is clearly visible, but the counter trigger (trigger function) doesn't run.
Emulator output:
...
{"verifications":{"app":"MISSING","auth":"MISSING"},"logging.googleapis.com/labels":{"firebase-log-type":"callable-request-verification"},"severity":"INFO","message":"Callable request verification passed"}
[function-addConversations] New Conversation [Test Conversation Topic] added
Profile updated
(print profile data)
...
What I SHOULD expect to see:
...
{"verifications":{"app":"MISSING","auth":"MISSING"},"logging.googleapis.com/labels":{"firebase-log-type":"callable-request-verification"},"severity":"INFO","message":"Callable request verification passed"}
[function-addConversations] New Conversation [Test Conversation Topic] added
Conversation Collection Changed // this is output by the trigger
Profile updated
(print profile data)
...
Did I do something wrong?
The issue was one closer to home.
I am developing using the firebase emulators and connecting to them using the emulators addition in Flutter's firebase packages built in emulator feature.
Firebase emulator setup e.g. Firebase Functions can be found here
TL;DR:
When starting your Firebase emulator, you should see something similar to:
C:\Users\User\MyApp\awesome-app-server>firebase emulators:start --only functions
i emulators: Starting emulators: functions
! functions: You are running the functions emulator in debug mode (port=9229). This means that functions will execute in sequence rather than in parallel.
! functions: The following emulators are not running, calls to these services from the Functions emulator will affect production: auth, firestore, database, hosting, pubsub, storage
! Your requested "node" version "12" doesn''t match your global version "14"
i ui: Emulator UI logging to ui-debug.log
i functions: Watching "C:\Users\User\MyApp\awesome-app-server\functions" for Cloud Functions...
> Debugger listening on ws://localhost:9229/03dc1d62-f2a3-418e-a343-bb0b357f7329
> Debugger listening on ws://localhost:9229/03dc1d62-f2a3-418e-a343-bb0b357f7329
> For help, see: https://nodejs.org/en/docs/inspector
! functions: The Cloud Firestore emulator is not running, so calls to Firestore will affect production.
! functions: The Realtime Database emulator is not running, so calls to Realtime Database will affect production.
...
BUT then you see this - THIS is very important!
i functions[us-central1-api-user-onConversationProfileCollectionCreate ]: function ignored because the database emulator does not exist or is not running.
This, since Firestore & (Realtime) Database use triggers which are found in functions, functions expects to find a local firestore/database emulator.
Since no firestore/database emulators were running locally
! functions: The Cloud Firestore emulator is not running, so calls to Firestore will affect production.
! functions: The Realtime Database emulator is not running, so calls to Realtime Database will affect production.
and these functions don't automagically attach to production Firestore/Database(s) (that would be potentially devestating), these triggers didn't run when I expected them to while emulating locally.
Solution:
Emulate firestore & database locally (see this to import your firestore data to a local emulator) with firebase emulators:start --only functions,...,firestore,database
Upload functions to work with Firestore/Database(s) (please do this with care)
More details:
Below I provide details what lead me to the problem, and how I figured out the issue:
What I was doing:
firebase emulators:start --inspect-functions --only functions,auth
[local] Firebase Functions I was developing and testing the backend for my mobile app using Firebase Functions for various interactivity.
Since Firebase Auth is handled locally through the use of custom tokens on my firebase functions app, I used local auth for testing
I had prepopulated my Firestore with data, thus I intended to use Firestore data while emulating locally which had worked as expected, BUT the firebase firestore & database triggers won't work due to emulating them locally.
I knew some of my triggers DID infact trigger correctly, thus it must be a configuration error of some kind.
Extended Solution (import firestore data to local emulator)
firebase emulators:start --inspect-functions --only functions,auth,firestore,database
notice the last 2, firestore & database - this should be added for triggers to work locally
I noticed when reviewing the logs at startup, the text indicating some functions won't run. This made me realize I was making a crucial mistake - the cause.
Import Data from production firestore
The reason for using production (during development) firestore is to not recreate all the data for each test. The solution is to export from firestore and import you data to the emulators.
See this for details - I wasn't able to export from the terminal, I thus went to Google Console to export to a storage bucket, and download it via the console from there.
To start the emulator (and allow debugging of firebase functions), I use:
firebase emulators:start --inspect-functions --import ./functions/{path/to/data} --only functions,auth,firestore,database
details:
firebase emulators:start - start emulator
--inspect-functions allow debugging functions via websockets - e.g. Webstorm NodeJs debugger on port 9229
--import ./functions/{path/to/data} - import data to firestore emulator using project root as ./
--only functions,auth,firestore,database specify the modules to emulate
You are using the wrong value. snapshot works only for onDelete and onCreate method
exports.useWildcard = functions.firestore
.document('users/{userId}')
.onWrite((change, context) => {
// If we set `/users/marie` to {name: "Marie"} then
context.params.userId == "marie"
// ... and ...
change.after.data() == {name: "Marie"}
});
for more info read here feedbackCloud Firestore triggers
I have built a simple Node.JS application that consists of a single API endpoint. Deployment works and I can all the API endpoint as expected. However the handler function also runs on deployment of the code, which is problematic for me as handler function posts a tweet to twitter, and I don't want it to tweet every time I deploy a code update. I haven't been able to find anyone online reporting a similar problem, but I'm sure this would not be expected functionality.
This is my serverless.yml file (I created originally from the GitLab Node.JS serverless template here https://gitlab.com/gitlab-org/project-templates/serverless-framework/):
service: my-project
provider:
name: aws
region: ${env:AWS_REGION}
runtime: nodejs10.x
plugins:
- serverless-offline
- serverless-jest-plugin
- serverless-stack-output # Allows us to output endpoint url to json file
functions:
post:
handler: main.main
events:
- http:
path: post
method: post
custom:
output:
handler: main.main
file: stack.json
I believe the problem in your case is the fact that the handler you have for serverless-stack-output plugin is the same as for your function. That plugin explicitly calls the handler (https://github.com/sbstjn/serverless-stack-output#handler) which executes your function. If you will drop use of the plugin or just configure a different handler for it the problem should disappear
I'm looking to make a basic scheduled function for firebase, however I'm using the inline editor on Cloud Functions. For now I'm just testing the basic example they provided in the docs:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
exports.scheduledFunctionCrontab = functions.pubsub.schedule('45 12 * * *').onRun((context) => {
console.log('This function was successful');
});
The part I am more confused about is what type of function do you use if your using the inline editor/ the console? (ie: HTTP, Pub Sub, Firestore ect.) to get this function running (in this example) 12:45 pm daily?
Thanks for the help or suggestions!
>>>Note: I'm aware that you can just manually make scheduled functions using the console, however this part of the application requires more flexibility and this syntax / method should work a lot better.
The inline editor in the Cloud Functions console isn't supported for editing functions that are defined by the firebase-functions module, and deployed by the Firebase CLI. The Firebase CLI does extra work that the Cloud console doesn't simulate. If you want to use scheduled functions correctly, you have to deploy with the CLI.
I am attempting to set a NodeJS Lambda function to be triggered when an image is uploaded to an Amazon S3 bucket. I have seen multiple tutorials and have the yml file set up as shown. Below is the YML config file:
functions:
image-read:
handler: handler.imageRead
events:
- s3:
bucket: <bucket-name-here>
event: s3:ObjectCreated:*
Is there something I am missing for the configuration? Is there something I need to do in an IAM role to set this up properly?
The YAML that you have here looks good but there may be some other problems.
Just to get you started:
are you deploying the function using the right credentials? (I've seen it many times that people are deploying in some other account etc. than they think - verify in the web console that it's there)
can you invoke the function in some other way? (from the serverless command line, using http trigger etc.)
do you see anything in the logs of that function? (add console.log statements to see if anything is being run)
do you see the trigger installed in the web console?
can you add trigger manually on the web console?
Try to add a simple function that would only print some logs when it is run and try to add a trigger for that function manually. If it works then try to do the same with the serverless command line but start with a simple function with just one log statement and if it works then go from there.
See also this post for more hints - S3 trigger is not registered after deployment:
https://forum.serverless.com/t/s3-trigger-is-not-registered-after-deployment/1858
When I run firebase deploy I get this error message:
functions: HTTP Error: 400, Change of function trigger type or event provider is not allowed
TL;DR
firebase functions:delete yourFunction // this can be done via the Firebase Console as well
firebase deploy
Explanation
Basically, Cloud Functions expects the same trigger for every function all the time, i.e. once it is created it has to stick to its original trigger because every function name is connected to a specific trigger. The trigger can therefore only be changed by deleting the function first and then creating it again with a different trigger.
This can now be done easily by using the functions:delete command:
firebase functions:delete yourFunction
The documentation features more advanced use cases as well.
Old solution
Solution of this is basically commenting or cutting out your function and then saving the Functions file and deploying. The function will get deleted in Firebase, but after that you can insert/uncomment your function and it will deploy just fine again. This error occurs when you take a function and change the type of trigger that it uses, i.e. HTTP, database or authentication.
Firstly cut it out
/* exports.yourFunction = someTrigger... */
And then, after deploying ("firebase deploy") replace your trigger
exports.yourFunction = anotherTrigger...
For those who stumble upon this in the future, the Cloud Functions console now offers a delete button.
You can also go to the Cloud Functions panel in the Google Cloud Platform console and delete your function from there. After that you can upload the function normally from firebase CLI. Not sure why they don't have a delete function option in the firebase console.