185 lines
6.0 KiB
Markdown
185 lines
6.0 KiB
Markdown
# 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`.
|
|
|
|
```typescript
|
|
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
|
|
|
|
```typescript
|
|
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
|
|
|
|
```typescript
|
|
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
|
|
|
|
```json
|
|
{
|
|
"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
|
|
|
|
```typescript
|
|
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}`);
|
|
}
|
|
);
|
|
```
|