Firebase Triggered Functions
Triggered functions run automatically in response to events in your Firebase project — no HTTP request required. When a Firestore document changes, a user account is created, or a file is uploaded to Storage, Firebase fires the matching trigger and runs your function code. This powers automation in your app without any user action.
Firestore Triggers
Firestore triggers fire when documents are created, updated, or deleted. They give you access to the document's data before and after the change.
const { onDocumentCreated, onDocumentUpdated, onDocumentDeleted }
= require("firebase-functions/v2/firestore");
const admin = require("firebase-admin");
admin.initializeApp();
// Runs every time a new post is created
exports.onPostCreated = onDocumentCreated("posts/{postId}", async (event) => {
const newPost = event.data.data();
const postId = event.params.postId;
console.log("New post created:", postId, newPost.title);
// Send notification to followers
await notifyFollowers(newPost.authorId, newPost.title);
});
// Runs every time a post is updated
exports.onPostUpdated = onDocumentUpdated("posts/{postId}", async (event) => {
const before = event.data.before.data();
const after = event.data.after.data();
if (before.likesCount !== after.likesCount) {
console.log("Likes changed from", before.likesCount, "to", after.likesCount);
}
});
// Runs every time a post is deleted
exports.onPostDeleted = onDocumentDeleted("posts/{postId}", async (event) => {
const deletedPost = event.data.data();
const postId = event.params.postId;
// Clean up related data
await deletePostComments(postId);
await deletePostFiles(postId);
console.log("Cleaned up after post:", postId);
});
Handling Any Document Change
const { onDocumentWritten } = require("firebase-functions/v2/firestore");
// Fires on create, update, OR delete
exports.onPostChanged = onDocumentWritten("posts/{postId}", async (event) => {
const postId = event.params.postId;
if (!event.data.after.exists) {
console.log("Post was deleted:", postId);
} else if (!event.data.before.exists) {
console.log("Post was created:", postId);
} else {
console.log("Post was updated:", postId);
}
});
Authentication Triggers
Auth triggers run when user accounts are created or deleted. Use them to set up initial data for new users or clean up data when users leave.
const { beforeUserCreated } = require("firebase-functions/v2/identity");
const { onDocumentDeleted: onUserDeleted }
= require("firebase-functions/v2/firestore");
// Runs when a new user registers
const { beforeUserCreated: authOnCreate } = require("firebase-functions/v2/identity");
// Simpler auth trigger using v1 syntax (still valid)
const functions = require("firebase-functions");
exports.onUserCreated = functions.auth.user().onCreate(async (user) => {
const db = admin.firestore();
// Create a user profile document
await db.collection("users").doc(user.uid).set({
email: user.email,
displayName: user.displayName || "New User",
createdAt: admin.firestore.FieldValue.serverTimestamp(),
plan: "free"
});
console.log("Profile created for:", user.email);
});
exports.onUserDeleted = functions.auth.user().onDelete(async (user) => {
const db = admin.firestore();
// Delete user's Firestore data
await db.collection("users").doc(user.uid).delete();
await db.collection("settings").doc(user.uid).delete();
console.log("Cleaned up data for:", user.email);
});
Storage Triggers
Storage triggers fire when files are uploaded or deleted. Use them to generate thumbnails, validate file contents, or update Firestore with the file's metadata.
const { onObjectFinalized, onObjectDeleted }
= require("firebase-functions/v2/storage");
// Runs when a file finishes uploading
exports.onFileUploaded = onObjectFinalized(async (event) => {
const filePath = event.data.name;
const contentType = event.data.contentType;
const fileSize = event.data.size;
console.log("File uploaded:", filePath);
console.log("Type:", contentType, "Size:", fileSize, "bytes");
// Generate thumbnail if it's an image
if (contentType.startsWith("image/")) {
await generateThumbnail(filePath);
}
});
// Runs when a file is deleted
exports.onFileDeleted = onObjectDeleted(async (event) => {
const filePath = event.data.name;
console.log("File deleted:", filePath);
// Update Firestore to remove the file reference
});
Updating the Document That Triggered the Function
Updating the same document that triggered the function causes the trigger to fire again, creating an infinite loop. Prevent this by checking whether the value already changed:
exports.addTimestamp = onDocumentCreated("posts/{postId}", async (event) => {
const data = event.data.data();
// Only update if processedAt is not already set
if (!data.processedAt) {
await event.data.ref.update({
processedAt: admin.firestore.FieldValue.serverTimestamp()
});
}
});
Wildcard Parameters in Trigger Paths
Wildcard segments in the trigger path (like {postId}) capture the actual value and make it available through event.params:
// Trigger path with two wildcards
exports.onCommentCreated = onDocumentCreated(
"posts/{postId}/comments/{commentId}",
async (event) => {
const postId = event.params.postId;
const commentId = event.params.commentId;
console.log("New comment", commentId, "on post", postId);
}
);
Key Takeaway
Triggered functions automate backend logic in response to Firestore changes, user account events, and Storage uploads. Use Firestore triggers to react to document changes, auth triggers to set up or clean up user data, and Storage triggers to process uploaded files. Prevent infinite loops when updating the document that triggered the function by checking whether the update is necessary before applying it.
