Last active
October 31, 2023 22:15
-
-
Save mplatts/711490535fde55c97e69766cba082db8 to your computer and use it in GitHub Desktop.
Firebase functions cheatsheet
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Core | |
const functions = require('firebase-functions'); | |
const admin = require('firebase-admin'); | |
admin.initializeApp(functions.config().firebase); | |
const db = admin.database(); | |
admin.auth().createUser({ | |
uid: uid, | |
displayName: displayName, | |
photoURL: photoURL | |
}); | |
admin.auth().getUser(uid) | |
// Triggers | |
functions.database.ref('/queues/images/tasks/{taskId}').onWrite((event) => {}) | |
event.timestamp // current time | |
event.params.userId | |
event.data == DeltaSnapshot | |
event.data.adminRef // ref that you can do anything with (rules don't apply) | |
event.data.current // snap() | |
event.data.key | |
event.data.previous // snap() before the write | |
event.data.ref | |
event.data.changed() // bool - has something changed in this write? | |
event.data.exists() // does data exist here? | |
event.data.exportVal() // returns JSON value | |
event.data.forEach((childSnap) => {}) // enumerates over children snaps | |
event.data.hasChild(path) // does that path have a child? | |
event.data.hasChildren() // bool | |
event.data.numChildren() // count children | |
event.data.toJSON() // not sure how this is different to exportVa() | |
event.data.val() | |
// Auth | |
functions.auth.user().onCreate((event) => {}) | |
functions.auth.user().onDelete((event) => {}) | |
event.data == UserRecord | |
event.data.uid | |
event.data.displayName | |
// HTTP | |
exports.date = functions.https.onRequest((req, res) => { | |
const formattedDate = moment().format(format); | |
console.log('Sending Formatted date:', formattedDate); | |
res.status(200).send(formattedDate); | |
}) | |
// Add env vars | |
firebase functions:config:set someservice.key="THE API KEY" someservice.id="THE CLIENT ID" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
exports.countlikechange = functions.database.ref('/posts/{postid}/likes/{likeid}').onWrite(event => { | |
const collectionRef = event.data.ref.parent; | |
const countRef = collectionRef.parent.child('likes_count'); | |
// Return the promise from countRef.transaction() so our function | |
// waits for this async event to complete before it exits. | |
return countRef.transaction(current => { | |
if (event.data.exists() && !event.data.previous.exists()) { | |
return (current || 0) + 1; | |
} | |
else if (!event.data.exists() && event.data.previous.exists()) { | |
return (current || 0) - 1; | |
} | |
}); | |
}); | |
// If the number of likes gets deleted, recount the number of likes | |
exports.recountlikes = functions.database.ref('/posts/{postid}/likes_count').onWrite(event => { | |
if (!event.data.exists()) { | |
const counterRef = event.data.ref; | |
const collectionRef = counterRef.parent.child('likes'); | |
// Return the promise from counterRef.set() so our function | |
// waits for this async event to complete before it exits. | |
return collectionRef.once('value') | |
.then(messagesData => counterRef.set(messagesData.numChildren())); | |
} | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const functions = require('firebase-functions'), | |
admin = require('firebase-admin'), | |
logging = require('@google-cloud/logging')(); | |
admin.initializeApp(functions.config().firebase); | |
const stripe = require('stripe')(functions.config().stripe.token), | |
currency = functions.config().stripe.currency || 'USD'; | |
// [START chargecustomer] | |
// Charge the Stripe customer whenever an amount is written to the Realtime database | |
exports.createStripeCharge = functions.database.ref('/stripe_customers/{userId}/charges/{id}').onWrite(event => { | |
const val = event.data.val(); | |
// This onWrite will trigger whenever anything is written to the path, so | |
// noop if the charge was deleted, errored out, or the Stripe API returned a result (id exists) | |
if (val === null || val.id || val.error) return null; | |
// Look up the Stripe customer id written in createStripeCustomer | |
return admin.database().ref(`/stripe_customers/${event.params.userId}/customer_id`).once('value').then(snapshot => { | |
return snapshot.val(); | |
}).then(customer => { | |
// Create a charge using the pushId as the idempotency key, protecting against double charges | |
const amount = val.amount; | |
const idempotency_key = event.params.id; | |
let charge = {amount, currency, customer}; | |
if (val.source !== null) charge.source = val.source; | |
return stripe.charges.create(charge, {idempotency_key}); | |
}).then(response => { | |
// If the result is seccessful, write it back to the database | |
return event.data.adminRef.set(response); | |
}, error => { | |
// We want to capture errors and render them in a user-friendly way, while | |
// still logging an exception with Stackdriver | |
return event.data.adminRef.child('error').set(userFacingMessage(error)).then(() => { | |
return reportError(error, {user: event.params.userId}); | |
}); | |
} | |
); | |
}); | |
// [END chargecustomer]] | |
// When a user is created, register them with Stripe | |
exports.createStripeCustomer = functions.auth.user().onCreate(event => { | |
const data = event.data; | |
return stripe.customers.create({ | |
email: data.email | |
}).then(customer => { | |
return admin.database().ref(`/stripe_customers/${data.uid}/customer_id`).set(customer.id); | |
}); | |
}); | |
// Add a payment source (card) for a user by writing a stripe payment source token to Realtime database | |
exports.addPaymentSource = functions.database.ref('/stripe_customers/{userId}/sources/{pushId}/token').onWrite(event => { | |
const source = event.data.val(); | |
if (source === null) return null; | |
return admin.database().ref(`/stripe_customers/${event.params.userId}/customer_id`).once('value').then(snapshot => { | |
return snapshot.val(); | |
}).then(customer => { | |
return stripe.customers.createSource(customer, {source}); | |
}).then(response => { | |
return event.data.adminRef.parent.set(response); | |
}, error => { | |
return event.data.adminRef.parent.child('error').set(userFacingMessage(error)).then(() => { | |
return reportError(error, {user: user}); | |
}); | |
}); | |
}); | |
// When a user deletes there account, clean up after them | |
exports.cleanupUser = functions.auth.user().onDelete(event => { | |
return admin.database().ref(`/stripe_customers/${event.data.uid}`).once('value').then(snapshot => { | |
return snapshot.val(); | |
}).then(customer => { | |
return stripe.customers.del(customer); | |
}).then(() => { | |
return admin.database().ref(`/stripe_customers/${event.data.uid}`).remove(); | |
}); | |
}); | |
// To keep on top of errors, we should raise a verbose error report with Stackdriver rather | |
// than simply relying on console.error. This will calculate users affected + send you email | |
// alerts, if you've opted into receiving them. | |
// [START reporterror] | |
function reportError(err, context = {}) { | |
// This is the name of the StackDriver log stream that will receive the log | |
// entry. This name can be any valid log stream name, but must contain "err" | |
// in order for the error to be picked up by StackDriver Error Reporting. | |
const logName = 'errors'; | |
const log = logging.log(logName); | |
// https://cloud.google.com/logging/docs/api/ref_v2beta1/rest/v2beta1/MonitoredResource | |
const metadata = { | |
resource: { | |
type: 'cloud_function', | |
labels: { function_name: process.env.FUNCTION_NAME } | |
} | |
}; | |
// https://cloud.google.com/error-reporting/reference/rest/v1beta1/ErrorEvent | |
const errorEvent = { | |
message: err.stack, | |
serviceContext: { | |
service: process.env.FUNCTION_NAME, | |
resourceType: 'cloud_function' | |
}, | |
context: context | |
}; | |
// Write the error log entry | |
return new Promise((resolve, reject) => { | |
log.write(log.entry(metadata, errorEvent), error => { | |
if (error) { reject(error); } | |
resolve(); | |
}); | |
}); | |
} | |
// [END reporterror] | |
// Sanitize the error message for the user | |
function userFacingMessage(error) { | |
return error.type ? error.message : 'An error occurred, developers have been alerted'; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment