Books/Firebase Essentials/Cloud Functions and Storage

    Cloud Functions and Storage

    Cloud Functions and Storage

    So far, everything has run in the browser (frontend). But sometimes you need code that runs on a server — sending emails, processing payments, resizing images, or running scheduled tasks. That's where Cloud Functions come in. And when your app needs to handle file uploads, Cloud Storage is the answer.

    Part 1: Cloud Functions

    What Are Cloud Functions?

    Cloud Functions are bits of server-side code that run in response to events. You write the function, deploy it to Firebase, and it runs automatically when triggered.

    Think of them as "if this happens, do that" — but on a server:

    TriggerExample
    HTTP requestSomeone visits a URL or calls an API
    Firestore changeA document is created, updated, or deleted
    AuthenticationA new user signs up
    Scheduled (cron)Run every day at midnight
    StorageA file is uploaded

    Setting Up Cloud Functions

    # If you didn't select Functions during firebase init:
    firebase init functions

    Choose TypeScript when asked. This creates a functions/ directory:

    functions/
    ├── src/
    │   └── index.ts          # Your functions go here
    ├── package.json
    └── tsconfig.json
    

    Install dependencies:

    cd functions
    npm install

    Your First HTTP Function

    // functions/src/index.ts
    import { onRequest } from "firebase-functions/v2/https";
    
    export const helloWorld = onRequest((request, response) => {
      response.json({ message: "Hello from Firebase!" });
    });

    Deploy and test:

    firebase deploy --only functions

    Firebase gives you a URL like: https://helloworld-abc123.a.run.app

    Visit that URL and you'll see { "message": "Hello from Firebase!" }.

    Firestore Trigger Functions

    Run code automatically when Firestore data changes:

    import { onDocumentCreated, onDocumentUpdated, onDocumentDeleted }
      from "firebase-functions/v2/firestore";
    
    // When a new user signs up, create a welcome notification
    export const onUserCreated = onDocumentCreated("users/{userId}", (event) => {
      const newUser = event.data?.data();
      console.log("New user:", newUser?.name);
    
      // Create a welcome notification
      return event.data?.ref.firestore
        .collection("notifications")
        .add({
          userId: event.params.userId,
          message: `Welcome, ${newUser?.name}!`,
          createdAt: new Date(),
        });
    });
    
    // When an order is updated, check if it was completed
    export const onOrderUpdated = onDocumentUpdated("orders/{orderId}", (event) => {
      const before = event.data?.before.data();
      const after = event.data?.after.data();
    
      if (before?.status !== "completed" && after?.status === "completed") {
        // Send confirmation email, update inventory, etc.
        console.log("Order completed:", event.params.orderId);
      }
    });

    Scheduled Functions

    Run code on a schedule (like a cron job):

    import { onSchedule } from "firebase-functions/v2/scheduler";
    
    // Run every day at midnight
    export const dailyCleanup = onSchedule("every day 00:00", async (event) => {
      // Clean up old data, send daily reports, etc.
      console.log("Running daily cleanup...");
    });
    
    // Run every hour
    export const hourlyCheck = onSchedule("every 60 minutes", async (event) => {
      console.log("Hourly check running...");
    });

    Callable Functions (Frontend Calls Backend)

    These are functions your frontend can call directly, with built-in authentication:

    // Backend — functions/src/index.ts
    import { onCall, HttpsError } from "firebase-functions/v2/https";
    
    export const processPayment = onCall(async (request) => {
      // request.auth contains the calling user's info
      if (!request.auth) {
        throw new HttpsError("unauthenticated", "Must be logged in");
      }
    
      const { amount, currency } = request.data;
    
      // Process payment...
      return { success: true, transactionId: "txn_123" };
    });
    // Frontend — calling the function
    import { getFunctions, httpsCallable } from "firebase/functions";
    
    const functions = getFunctions();
    const processPayment = httpsCallable(functions, "processPayment");
    
    const result = await processPayment({ amount: 1999, currency: "usd" });
    console.log(result.data); // { success: true, transactionId: "txn_123" }

    What to ask your AI: "Create a Cloud Function that [does something] when [trigger event]. Use Firebase Functions v2 with TypeScript."

    Testing Locally with the Emulator

    Before deploying, test functions locally:

    firebase emulators:start --only functions

    This runs your functions on your computer. The emulator URL (usually http://localhost:5001) lets you test without deploying.

    Deploy Functions

    # Deploy all functions
    firebase deploy --only functions
    
    # Deploy a specific function
    firebase deploy --only functions:helloWorld

    Part 2: Cloud Storage

    What is Cloud Storage?

    Cloud Storage is where your app stores files — images, PDFs, videos, user uploads, etc. It's like a file system in the cloud.

    Setting Up Storage

    1. In Firebase Console → StorageGet started
    2. Choose security rules (start with test mode for learning)
    3. Choose a location

    Upload Files from the Frontend

    import { storage } from "@/lib/firebase";
    import { ref, uploadBytes, getDownloadURL } from "firebase/storage";
    
    // Upload a file
    async function uploadFile(file: File, path: string): Promise<string> {
      const storageRef = ref(storage, path);
      const snapshot = await uploadBytes(storageRef, file);
      const downloadUrl = await getDownloadURL(snapshot.ref);
      return downloadUrl;
    }
    
    // Example: Upload a profile picture
    const handleFileUpload = async (event: React.ChangeEvent<HTMLInputElement>) => {
      const file = event.target.files?.[0];
      if (!file) return;
    
      const path = `profiles/${userId}/${Date.now()}-${file.name}`;
      const url = await uploadFile(file, path);
    
      // Save the URL to Firestore
      await updateDoc(doc(db, "users", userId), {
        avatarUrl: url,
      });
    };

    Upload with Progress Tracking

    import { ref, uploadBytesResumable, getDownloadURL } from "firebase/storage";
    
    function uploadWithProgress(file: File, path: string, onProgress: (percent: number) => void) {
      const storageRef = ref(storage, path);
      const uploadTask = uploadBytesResumable(storageRef, file);
    
      return new Promise<string>((resolve, reject) => {
        uploadTask.on(
          "state_changed",
          (snapshot) => {
            const percent = (snapshot.bytesTransferred / snapshot.totalBytes) * 100;
            onProgress(percent);
          },
          (error) => reject(error),
          async () => {
            const url = await getDownloadURL(uploadTask.snapshot.ref);
            resolve(url);
          }
        );
      });
    }
    
    // Usage in a React component
    const [progress, setProgress] = useState(0);
    
    const handleUpload = async (file: File) => {
      const url = await uploadWithProgress(
        file,
        `uploads/${file.name}`,
        (percent) => setProgress(percent)
      );
      console.log("Uploaded! URL:", url);
    };

    Delete Files

    import { ref, deleteObject } from "firebase/storage";
    
    async function deleteFile(path: string): Promise<void> {
      const storageRef = ref(storage, path);
      await deleteObject(storageRef);
    }

    List Files in a Folder

    import { ref, listAll, getDownloadURL } from "firebase/storage";
    
    async function listFiles(folderPath: string) {
      const folderRef = ref(storage, folderPath);
      const result = await listAll(folderRef);
    
      const urls = await Promise.all(
        result.items.map(item => getDownloadURL(item))
      );
    
      return urls;
    }
    
    // Get all images in a user's folder
    const images = await listFiles(`profiles/${userId}`);

    Storage Security Rules

    Like Firestore, Storage has security rules (storage.rules):

    rules_version = '2';
    service firebase.storage {
      match /b/{bucket}/o {
    
        // Users can only upload to their own folder
        match /profiles/{userId}/{allPaths=**} {
          allow read: if true;
          allow write: if request.auth != null && request.auth.uid == userId;
        }
    
        // Anyone can read public files
        match /public/{allPaths=**} {
          allow read: if true;
          allow write: if request.auth != null;
        }
      }
    }
    

    What to ask your AI: "Write Storage security rules that let users upload to their own folder and let everyone read public files."

    Storage File Organization

    Organize your files with clear paths:

    storage/
    ├── profiles/
    │   ├── userId1/
    │   │   └── avatar.jpg
    │   └── userId2/
    │       └── avatar.jpg
    ├── posts/
    │   ├── postId1/
    │   │   ├── cover.jpg
    │   │   └── attachment.pdf
    │   └── postId2/
    │       └── cover.jpg
    └── public/
        ├── logo.png
        └── banner.jpg
    

    What to ask your AI: "Create a file upload component with drag-and-drop, progress bar, and preview. Store files in Firebase Storage and save URLs in Firestore."

    What's Next?

    You've covered all the major Firebase services. The final tutorial is your Firebase Cheat Sheet — a quick reference with commands, patterns, and AI prompts for every Firebase task.


    🌐 www.genai-mentor.ai