MotorSignal-App/.claude/skills/firebase-data-connect/reference/cloud_functions.md

6.0 KiB

Cloud Functions Integration Reference

Use this reference to handle database events in SQL Connect by triggering Cloud Functions in response to mutation executions.


Core Trigger Configuration

To handle a mutation execution, define the onMutationExecuted event handler.

🚨 Critical Infinite Loop Constraint

Unlike document-based database triggers (like Firestore or Realtime Database), SQL Connect event triggers do not provide a "before" snapshot of the data. Because SQL Connect proxies requests directly to PostgreSQL, "before" states cannot be resolved transactionally.

  • Warning: If onMutationExecuted executes a SQL Connect mutation, it can trigger another onMutationExecuted trigger in a cascading loop. Make sure that onMutationExecuted has a filter on operation to reduce the chance of infinite loops.
  • Rule: Ensure that no mutation executed inside the function can ever trigger the handler itself, even indirectly.

Location & Region Matching Rule

The Cloud Function region option must match your SQL Connect service location. You must explicitly configure the region parameter (e.g., 'us-central1') in the trigger options to match the location specified in dataconnect.yaml.

import { onMutationExecuted } from "firebase-functions/dataconnect";
import { logger } from "firebase-functions";

export const logMutation = onMutationExecuted(
  {
    region: "europe-west1" // Must match the SQL Connect service location
  },
  (event) => {
    logger.info("A mutation was executed!", {
      eventId: event.id,
      type: event.type
    });
  }
);

Event Filtering

To prevent unnecessary function invocations and infinite execution loops, always specify narrow filters using service and operation attributes.

  • service & operation (Recommended): Always specify these to restrict the trigger to a specific mutation in your project.
  • connector (Optional): Can be omitted if you want to trigger on the same operation name across multiple connectors. Specify it only if you need to restrict the trigger to a specific connector.

Comprehensive Example

import { onMutationExecuted } from "firebase-functions/dataconnect";
import { logger } from "firebase-functions";

// Triggers for "CreateUser" mutation in "myAppService" service.
// 'connector' is omitted (optional), meaning it matches "CreateUser" in any connector.
export const onUserCreate = onMutationExecuted(
  {
    service: "myAppService",
    operation: "CreateUser",
    // region: "us-central1" // Optional: defaults to us-central1, change if database is elsewhere
  },
  (event) => {
    logger.info("A new user was created!");
  }
);

// Advanced: Trigger using wildcards or capture variables
export const onMutationCaptures = onMutationExecuted(
  {
    service: "myAppService",
    operation: "{operation}", // Captures matching operation name dynamically
  },
  (event) => {
    const triggeredOp = event.params.operation;
    logger.info(`Captured operation execution: ${triggeredOp}`);
  }
);

Accessing User Authentication Context

Extract security credentials about the caller who executed the mutation using event.authType and event.authId.

Auth Context Mappings

Triggered Principal event.authType event.authId
Authenticated end user "app_user" Firebase Auth token UID
Unauthenticated end user "unauthenticated" Empty
Admin SDK (Impersonating User) "app_user" Firebase Auth token UID of the impersonated user
Admin SDK (Impersonating Unauth) "unauthenticated" Empty
Admin SDK (Full privileges) "admin" Empty

Auth Extraction Example

export const processSensitiveMutation = onMutationExecuted(
  { operation: "UpdateFinancials" },
  (event) => {
    if (event.authType === "admin") {
      console.log("Elevated admin mutation execution.");
    } else {
      console.log(`Mutation initiated by user: ${event.authId}`);
    }
  }
);

Parsing Event Data Payloads

The trigger payload provides inputs passed to the mutation (payload.variables) and return values generated from the execution (payload.data).

Event Payload Structure

{
  "authType": "app_user",
  "authId": "user-123",
  "data": {
    "payload": {
      "variables": {
        "movieId": "m-1",
        "rating": 5
      },
      "data": {
        "review_insert": {
          "id": "r-99"
        }
      },
      "errors": []
    }
  }
}
  • event.data.payload.variables: Inputs passed to the mutation.
  • event.data.payload.data: Fields returned by the mutation execution.
  • event.data.payload.errors: Array of execution errors. Empty if successful.

Payload Extraction Example

import { onMutationExecuted } from "firebase-functions/dataconnect";
import { logger } from "firebase-functions";

export const onNewReview = onMutationExecuted(
  {
    service: "myAppService",
    connector: "reviews",
    operation: "CreateReview",
  },
  (event) => {
    // Extract input variables passed to the mutation
    const inputVariables = event.data.payload.variables;

    // Extract returned fields from the database write
    const returnedFields = event.data.payload.data;

    logger.info(`Processed review ${returnedFields.review_insert.id} for movie ${inputVariables.movieId}`);
  }
);