# Google Cloud Functions

- Tool: FlutterFlow
- Difficulty: Intermediate
- Time required: 45 minutes
- Last updated: July 2026

## TL;DR

Connect FlutterFlow to Google Cloud Functions using either Firebase Callable Functions (the FlutterFlow-native path, where the user's Firebase Auth token is passed automatically) or a plain HTTP-triggered function called via an API Call group. Cloud Functions are the canonical place to hide API keys, run Stripe charges, proxy database queries, and send FCM notifications — everything FlutterFlow's compiled Dart client cannot securely hold.

## Why Google Cloud Functions Are the Backbone of Secure FlutterFlow Apps

FlutterFlow compiles to a Flutter app that runs entirely on the user's device — iOS, Android, or web browser. This is powerful for building native-quality UIs visually, but it creates an immediate security constraint: anything you put in FlutterFlow, including API keys, database passwords, and secret tokens, becomes part of the compiled app binary. Anyone who downloads your app can extract those secrets. This is why every other integration guide in this category — Oracle, MySQL, SQL Server, Firebase Cloud Messaging — points to the same answer: put your privileged logic in a backend function.

Google Cloud Functions is the cleanest solution for a FlutterFlow project already connected to Firebase, which is the majority of serious FlutterFlow apps. Because Firebase and Google Cloud are the same platform, your FlutterFlow project's Firebase connection gives you free authentication context in every Cloud Function call. When you use a Callable Function, the signed-in user's Firebase ID token is verified automatically inside the function — you don't have to write token validation code or pass auth headers manually from FlutterFlow. This is meaningfully easier than the equivalent setup with Azure Functions or a standalone Express server.

Google Cloud Functions Gen 2 (the current standard) uses defineSecret to store sensitive values — your Stripe secret key, OpenAI API key, ORDS OAuth credentials, or any third-party token — in Google Secret Manager. These secrets are injected into the function at runtime and are never visible to FlutterFlow or the end-user app. The free tier covers 2 million invocations per month, making Cloud Functions cost-effective for most early-stage apps. Note that Cloud Functions requires the Firebase Blaze (pay-as-you-go) plan — but Google provides a free monthly usage grant, and the costs are tiny at prototype and small-scale production traffic. Set a billing budget alert immediately after upgrading to Blaze.

## Before you start

- A FlutterFlow project connected to Firebase (Settings & Integrations → Firebase — required for Callable Functions and Blaze plan access)
- A Google Cloud / Firebase project upgraded to the Blaze (pay-as-you-go) plan — Cloud Functions require Blaze; set a billing budget alert immediately
- Node.js 18+ or Python 3.11+ knowledge for writing function code (or use the Firebase console inline editor for simple functions)
- The Firebase CLI installed on your computer if you want to deploy functions locally (optional — the console editor works for simple functions)
- Any third-party API keys or secrets you want the function to hold (Stripe, OpenAI, database credentials, etc.)

## Step-by-step guide

### 1. Upgrade to Firebase Blaze and understand the two function types

Before writing your first Cloud Function, you need to upgrade your Firebase project to the Blaze plan. Go to console.firebase.google.com, select your project, and click the 'Upgrade' button in the bottom-left of the sidebar. Choose the Blaze (pay-as-you-go) plan. Critically, immediately after upgrading, click 'Budget Alerts' in the Firebase console and set a monthly budget limit with email alerts — Blaze has no default spending cap, and a runaway function can generate unexpected bills.

Now understand the two types of Cloud Functions you can call from FlutterFlow:

**Option A — Firebase Callable Functions**: These are the FlutterFlow-native choice. When your FlutterFlow project is connected to Firebase and a user is signed in, the Firebase SDK automatically passes the user's ID token with every Callable Function invocation. Inside the function, you call `context.auth` to verify the user without writing any token validation code. FlutterFlow invokes Callable Functions via a custom Dart action using the Firebase SDK (not a standard API Call). This is the most secure and least code-heavy option.

**Option B — HTTP-triggered Functions**: These are standard HTTPS endpoints callable from FlutterFlow's API Call panel as you would call any REST API. They're more flexible (can receive webhook events, integrate with third-party services, be called from any client), but you must write your own authentication verification inside the function — typically by extracting a Bearer token from the Authorization header and verifying it with the Firebase Admin SDK.

For most FlutterFlow apps where users sign in via Firebase Auth, Callable Functions are the recommended choice. For webhooks, public APIs, or non-Firebase-auth flows, use HTTP triggers.

**Expected result:** Your Firebase project shows 'Blaze' in the plan badge in the Firebase console. You've set a billing budget alert. You understand whether to build a Callable or HTTP-triggered function for your use case.

### 2. Write a Gen 2 Cloud Function with defineSecret for your API keys

Open the Firebase console, go to your project, and click 'Functions' in the left nav. Click 'Get Started' if this is your first function. In the Functions dashboard, you can write simple functions directly in the browser console editor, or deploy via the Firebase CLI for more complex setups.

For a Callable Function in the browser editor: click '+ Add Function', choose 'Callable', and paste the following starter code. The key pattern to understand is `defineSecret` — this references a value stored in Google Secret Manager, injected into the function at runtime. FlutterFlow and end users never see the secret value.

If you're using the Firebase CLI (recommended for anything beyond a simple test): create a `functions/` directory in your project, run `firebase init functions`, choose Node.js, then edit `functions/index.js`. Deploy with `firebase deploy --only functions`.

For HTTP-triggered functions called from FlutterFlow's API Calls panel, the function structure is similar but uses `onRequest` instead of `onCall`. Inside the function, verify the caller by checking the Authorization header against the Firebase Admin SDK's `verifyIdToken`.

Store any secret you'd normally put in an `.env` file — Stripe secret key, OpenAI key, database passwords — using `firebase functions:secrets:set SECRET_NAME` in the CLI, or via the Google Cloud Console → Secret Manager. Then reference it in your function with `defineSecret('SECRET_NAME')`.

```
// Firebase Gen 2 Cloud Function — Callable (Node.js)
// Deploy: firebase deploy --only functions
const { onCall, HttpsError } = require('firebase-functions/v2/https');
const { defineSecret } = require('firebase-functions/params');

// Secrets stored in Google Secret Manager, injected at runtime
const STRIPE_SECRET_KEY = defineSecret('STRIPE_SECRET_KEY');
const OPENAI_API_KEY = defineSecret('OPENAI_API_KEY');

// Example: Stripe PaymentIntent (Callable — auto-verified Firebase auth)
exports.createPaymentIntent = onCall(
  { secrets: [STRIPE_SECRET_KEY] },
  async (request) => {
    // Verify user is signed in (auto-passed by Firebase SDK)
    if (!request.auth) {
      throw new HttpsError('unauthenticated', 'Must be signed in.');
    }

    const { amount, currency } = request.data;
    if (!amount || amount < 50) {
      throw new HttpsError('invalid-argument', 'Amount must be at least 50 cents.');
    }

    const stripe = require('stripe')(STRIPE_SECRET_KEY.value());
    const paymentIntent = await stripe.paymentIntents.create({
      amount,
      currency: currency || 'usd',
      metadata: { firebase_uid: request.auth.uid }
    });

    return { clientSecret: paymentIntent.client_secret };
  }
);

// Example: HTTP-triggered function (for API Call panel in FlutterFlow)
exports.generateContent = onRequest(
  { secrets: [OPENAI_API_KEY], cors: ['https://app.flutterflow.io', 'https://your-published-domain.com'] },
  async (req, res) => {
    // Verify Firebase ID token from Authorization header
    const token = req.headers.authorization?.split('Bearer ')[1];
    if (!token) { res.status(401).json({ error: 'Unauthorized' }); return; }

    const admin = require('firebase-admin');
    if (!admin.apps.length) admin.initializeApp();
    await admin.auth().verifyIdToken(token); // throws if invalid

    const { prompt } = req.body;
    const response = await fetch('https://api.openai.com/v1/chat/completions', {
      method: 'POST',
      headers: { 'Authorization': `Bearer ${OPENAI_API_KEY.value()}`, 'Content-Type': 'application/json' },
      body: JSON.stringify({ model: 'gpt-4o-mini', messages: [{ role: 'user', content: prompt }] })
    });
    const data = await response.json();
    res.json({ content: data.choices[0].message.content });
  }
);
```

**Expected result:** After firebase deploy --only functions, your function URL appears in the Firebase console Functions dashboard. Clicking 'Test function' with sample data shows a successful response. The function logs show no errors in the Functions log viewer.

### 3. Call the function from FlutterFlow — HTTP trigger via API Calls

For HTTP-triggered Cloud Functions, you'll use FlutterFlow's API Call panel — the same workflow as any REST API integration.

In your FlutterFlow project, click 'API Calls' in the left navigation panel. Click '+ Add' → 'Create API Group'. Name it 'CloudFunctions'. In the Base URL field, enter the base URL for your functions: `https://{region}-{project-id}.cloudfunctions.net` — for example, `https://us-central1-my-firebase-project.cloudfunctions.net`. You can find the exact function URL in the Firebase console → Functions → the function's row → trigger URL.

Add a shared header to the group for authentication: Key = `Authorization`, Value = `Bearer {{authToken}}`. Add a group-level variable: `authToken` (String type). At runtime, you'll populate this with the signed-in user's Firebase ID token, which you can retrieve in FlutterFlow via a Custom Action using the Firebase Auth SDK (see tip below).

Now add individual API Calls inside the group. Click '+ Add API Call'. Name it 'GenerateContent'. Set Method to POST. Endpoint: `/generateContent` (the function name). In the Body tab, set type to JSON, and add a body variable: `{ "prompt": "{{prompt}}" }`. Add a `prompt` variable in the Variables tab (String type).

Test the call: in the 'Response & Test' tab, set the `authToken` to a real Firebase ID token (copy one from the Firebase Authentication Emulator or from a debug session) and the `prompt` to a sample string, then click 'Test API Call'. A successful 200 response with your function's JSON output confirms the integration is working.

Parse the response: click 'Generate from Response' to auto-create JSON Paths from the response structure. Map `$.content` to a String field in a simple Data Type, or extract individual fields if your function returns a structured object.

In your widget Action Flow: add a button, open Action Flow Editor → + Add Action → Backend/API Calls → select 'CloudFunctions' group → 'GenerateContent'. Map the `prompt` variable to a TextField's value and the `authToken` to your App State auth token variable.

```
// API Group configuration reference
{
  "group_name": "CloudFunctions",
  "base_url": "https://us-central1-your-project.cloudfunctions.net",
  "headers": [
    {
      "key": "Authorization",
      "value": "Bearer {{authToken}}"
    },
    {
      "key": "Content-Type",
      "value": "application/json"
    }
  ],
  "variables": [
    { "name": "authToken", "type": "String" }
  ],
  "api_calls": [
    {
      "name": "GenerateContent",
      "method": "POST",
      "endpoint": "/generateContent",
      "body": "{ \"prompt\": \"{{prompt}}\" }",
      "variables": [
        { "name": "prompt", "type": "String" }
      ],
      "json_paths": [
        { "path": "$.content", "field": "content", "type": "String" }
      ]
    }
  ]
}
```

**Expected result:** The API Call test returns a 200 response with your function's JSON output. The JSON path mapping shows the correct fields. When triggered from a widget action in Test Mode, the function executes and returns data that populates the UI.

### 4. Invoke Callable Functions from FlutterFlow via Custom Action (Dart)

Firebase Callable Functions are invoked differently from HTTP triggers — they use the Firebase SDK's `httpsCallable` method rather than a plain HTTPS request. FlutterFlow doesn't have a built-in UI for Callable Functions, so you'll add a Custom Action in Dart that wraps the SDK call.

In FlutterFlow, click 'Custom Code' in the left nav. Click '+ Add' → 'Action'. Name it 'CallCloudFunction' (or something specific like 'CreatePaymentIntent'). In the Dependencies field, the `cloud_functions` package is already included when your project is connected to Firebase — you don't need to add it separately.

Paste the Dart code in the code editor. The action takes your input data as arguments (defined in the Action Arguments panel), calls the Callable Function using FirebaseFunctions.instance.httpsCallable(), and returns the result. Map the return value to a FlutterFlow action output.

In the Action Arguments panel, add arguments matching your function's expected input — for example, `amount` (int) and `currency` (String) for a Stripe function. Add an Action Output: name it `clientSecret`, type String.

After writing the action, go to the widget where you want to trigger the payment, open Action Flow Editor, click '+ Add Action' → 'Custom Actions' → 'CreatePaymentIntent'. Map the widget's input values to the action arguments. After the action completes, use the `clientSecret` output in a subsequent action to open Stripe's payment sheet (using another Custom Action with the flutter_stripe package).

Note: Custom Actions do NOT run in FlutterFlow's browser-based 'Run Mode'. You must test Callable Function actions on a real device or using 'Local Run' with an iOS Simulator or Android Emulator. This is expected Flutter behavior — the Firebase SDK requires a real platform context.

```
// Custom Action Dart — invokes a Firebase Callable Function
// Dependencies: cloud_functions (included with Firebase FlutterFlow projects)
import 'package:cloud_functions/cloud_functions.dart';

Future<String> createPaymentIntent(
  int amount,
  String currency,
) async {
  try {
    final callable = FirebaseFunctions.instance
        .httpsCallable('createPaymentIntent');

    final result = await callable.call<Map<String, dynamic>>({
      'amount': amount,
      'currency': currency,
    });

    final data = result.data as Map<String, dynamic>;
    final clientSecret = data['clientSecret'] as String? ?? '';
    return clientSecret;
  } on FirebaseFunctionsException catch (e) {
    // e.code matches the HttpsError code thrown in the function
    // e.g. 'unauthenticated', 'invalid-argument', 'internal'
    throw Exception('Function error [${e.code}]: ${e.message}');
  } catch (e) {
    throw Exception('Unexpected error: $e');
  }
}
```

**Expected result:** After adding the Custom Action, it appears in the Action Flow Editor under 'Custom Actions'. In device testing (Local Run or real device), triggering the action successfully calls the Cloud Function and returns the output to the FlutterFlow action chain.

### 5. Parse the function response and bind to FlutterFlow widgets

Whether you're calling via API Calls (HTTP trigger) or a Custom Action (Callable), the Cloud Function returns a JSON object that you need to parse and bind to your FlutterFlow UI.

For HTTP trigger API Calls: The JSON paths you set up in Step 3 handle parsing automatically. The API Call's response is available in the Action Flow Editor as an 'Action Output'. To bind the response to a widget, go to the widget's properties, click the binding icon next to the relevant field (e.g. a Text widget's value), navigate to 'Action Outputs' → your API Call action → select the JSON path field.

For multi-field responses, create a FlutterFlow Data Type matching your function's return structure. For example, if your function returns `{ "title": "...", "body": "...", "score": 0.95 }`, create a Data Type 'GeneratedContent' with String fields title and body and a Double field score. Map JSON paths to this Data Type in the API Call configuration.

For Callable Function Custom Actions: your Dart action returns a single value (or you can return a JSON string and parse it in a follow-up action). If you need to return multiple fields, return a JSON string from Dart: `return jsonEncode({'title': title, 'body': body})` and use FlutterFlow's JSON parsing actions to extract fields.

For error handling in the UI: In the Action Flow Editor, after the API Call or Custom Action, add a conditional: check if the response was successful (HTTP status 200, or no exception thrown). On failure, show a Snackbar or alert dialog with the error message. Common failure modes to handle: 401 (token expired), 429 (rate limited by the third-party API your function calls), 500 (function threw an unhandled exception — check the Firebase Functions log).

For lists of data returned by the function, use a ListView with a 'Dynamic Children' binding to the API Call's list response. If the function returns a paginated list, add a 'Load More' button that passes an `offset` variable to the next API Call invocation.

**Expected result:** Widgets in your FlutterFlow app display data returned by the Cloud Function — text fields show generated content, lists populate with returned items, and form submissions trigger function calls that update data and show success/error feedback.

### 6. Handle cold starts, set min instances, and go to production

Cold starts are the most common user-experience issue with Cloud Functions: when a function hasn't been invoked recently, Google needs to spin up a new container, which adds 1–3 seconds of latency to the first request. For a FlutterFlow app with a sign-in flow, the first function call after login often hits a cold start.

To minimize cold start impact: always show a loading state in FlutterFlow before an API Call or Custom Action that triggers a Cloud Function. Add a circular progress indicator or skeleton screen that appears when the action starts and dismisses when the response arrives. In the Action Flow Editor: set a boolean App State variable `isLoading` to true before the call, then set it to false in both the success and error branches.

For hot paths (functions called on every app launch or payment submission), set a minimum instance count to eliminate cold starts. In your function configuration add `minInstances: 1`. This keeps one instance always warm. Note: minimum instances run continuously and are billed even when idle (though at the free tier's computation rates, one warm instance typically costs under $5/month).

CORS for web builds: if your FlutterFlow app has a web version (published to a custom domain), HTTP-triggered functions need the `cors` option set to your domain. In Gen 2 functions, pass `cors: ['https://your-domain.com', 'https://app.flutterflow.io']` in the onRequest options — as shown in the code block in Step 2. Callable Functions handle CORS automatically.

Before going to production: check the Firebase console → Functions → Usage to confirm invocation counts are within expectations. Set Cloud Monitoring alerts for high error rates or high invocation counts. Review the defineSecret approach for all secrets — no credentials should exist anywhere in your FlutterFlow project files or Dart code.

If you need help writing Cloud Functions for complex integrations — Stripe webhooks, multi-database orchestration, custom AI pipelines — RapidDev's team builds FlutterFlow + Cloud Functions setups every week. Book a free scoping call at rapidevelopers.com/contact.

```
// Gen 2 function with min instances and CORS (HTTP trigger)
const { onRequest } = require('firebase-functions/v2/https');
const { defineSecret } = require('firebase-functions/params');

const API_KEY = defineSecret('THIRD_PARTY_API_KEY');

exports.myFunction = onRequest(
  {
    secrets: [API_KEY],
    minInstances: 1,          // keep warm to avoid cold starts on hot paths
    maxInstances: 10,         // cap scaling to avoid runaway billing
    timeoutSeconds: 30,
    cors: [
      'https://app.flutterflow.io',
      'https://your-published-domain.com'
    ]
  },
  async (req, res) => {
    // Verify Firebase ID token
    const token = req.headers.authorization?.split('Bearer ')[1];
    if (!token) {
      res.status(401).json({ error: 'Missing auth token' });
      return;
    }

    try {
      const admin = require('firebase-admin');
      if (!admin.apps.length) admin.initializeApp();
      const decoded = await admin.auth().verifyIdToken(token);

      // Your privileged logic here
      const result = await callThirdPartyAPI(API_KEY.value(), req.body);
      res.json({ success: true, data: result });
    } catch (err) {
      console.error('Function error:', err);
      res.status(500).json({ error: 'Internal error' });
    }
  }
);
```

**Expected result:** Functions show green status in Firebase console with no cold-start complaints in user testing. The Firebase Functions usage dashboard shows invocation counts within the free tier grant. Web and mobile builds both call functions successfully.

## Best practices

- Use Firebase Callable Functions (not HTTP triggers) for user-authenticated operations — the Firebase SDK passes the user's ID token automatically, eliminating the need to manage and refresh auth headers manually in FlutterFlow
- Store ALL secrets with defineSecret (Gen 2) in Google Secret Manager — Stripe keys, OpenAI tokens, database credentials, third-party API keys — never in FlutterFlow App Values, Custom Action code, or environment variables visible to the client
- Set maxInstances on every function to cap scaling and prevent runaway billing — maxInstances: 10 is a safe default for most FlutterFlow apps; increase only when load testing confirms you need more
- Set a billing budget alert immediately after upgrading to the Blaze plan — Cloud Functions billing has no default cap, and unexpected traffic spikes can generate charges; set an alert at a comfortable threshold
- Always show a loading state in FlutterFlow before triggering a Cloud Function — cold starts add 1–3 seconds of latency; a loading spinner prevents users from thinking the app is broken
- Configure CORS explicitly for your published web domain in onRequest options — do not use cors: true in production; enumerate only the domains that need access
- Use Cloud Functions as the canonical proxy layer for all sensitive integrations — Oracle ORDS calls, Stripe charges, FCM sends, SQL Server queries, and OpenAI calls all belong in a function, not in FlutterFlow Dart code
- Check Firebase Functions Logs in the Firebase console as your first debugging step — most integration failures are visible there (third-party API errors, auth rejections, function exceptions) before they become mystery blank screens in FlutterFlow

## Use cases

### Stripe payment processing for a subscription app

A FlutterFlow SaaS app needs to charge users via Stripe. The Stripe secret key (sk_live_...) cannot be in the Dart client — a Cloud Function receives the payment method ID from FlutterFlow, creates a PaymentIntent server-side, and returns the client secret for the FlutterFlow Stripe UI to confirm. The function also creates Stripe customers and handles webhook events.

Prompt example:

```
Build a FlutterFlow subscription app where tapping 'Subscribe' calls a Firebase Cloud Function that creates a Stripe PaymentIntent and returns the client secret for in-app payment confirmation.
```

### AI-powered content generation app

A FlutterFlow content tool lets users generate copy with OpenAI's GPT API. The OpenAI API key lives in a Cloud Function via defineSecret — FlutterFlow sends the user's prompt and context, the function calls OpenAI, and streams back the generated text. User request counts are tracked in Firestore to enforce per-user quotas.

Prompt example:

```
Build a FlutterFlow app where users type a topic, tap Generate, and a Firebase Cloud Function calls the OpenAI API and returns AI-written content that displays in a text field.
```

### Multi-database proxy for a legacy system

A FlutterFlow enterprise app needs to read from an Oracle ORDS endpoint and write to Firestore simultaneously. A Cloud Function acts as the orchestration layer — it fetches Oracle data (using ORDS client credentials stored in Secret Manager), transforms the response, writes a cached copy to Firestore, and returns a unified JSON to FlutterFlow. No Oracle credentials ever touch the client.

Prompt example:

```
Build a FlutterFlow inventory app where a Cloud Function fetches product data from an Oracle ORDS endpoint, syncs it to Firestore, and returns the combined results to the app.
```

## Troubleshooting

### API Call returns 401 or 403 — function rejects the request as unauthorized

Cause: For HTTP triggers: the Firebase ID token in the Authorization header has expired (tokens are valid for 1 hour) or was not sent correctly. For Callable Functions: the user is not signed in to Firebase in the FlutterFlow app at the time of the call.

Solution: For HTTP triggers: implement token refresh in FlutterFlow. Create a Custom Action that calls FirebaseAuth.instance.currentUser?.getIdToken(true) (the true parameter forces a refresh) and updates your App State authToken variable. Call this Custom Action before any Cloud Function API Call in the Action Flow. For Callable Functions: add an Auth Guard condition in the Action Flow — check that the current user is not null before triggering the Callable action. If null, redirect to the login page.

### XMLHttpRequest error on FlutterFlow web preview — function call fails in the browser

Cause: CORS is not configured on the HTTP-triggered Cloud Function. The browser blocks cross-origin requests from the FlutterFlow preview domain (app.flutterflow.io) to your function URL. Native iOS/Android builds are unaffected.

Solution: Update your Cloud Function's onRequest options to include the cors parameter with FlutterFlow's domains: cors: ['https://app.flutterflow.io', 'https://your-published-domain.com']. Redeploy the function. Callable Functions handle CORS automatically and do not need this configuration. If you're in development and want to allow all origins temporarily (for testing only): cors: true — but restrict this before going to production.

```
// Add cors to your onRequest options
exports.myFn = onRequest(
  { cors: ['https://app.flutterflow.io', 'https://your-domain.com'] },
  async (req, res) => { /* ... */ }
);
```

### Function works in Firebase console test but Custom Action in FlutterFlow's Run Mode shows no response

Cause: Callable Function Custom Actions require a real platform context (iOS/Android device or simulator). FlutterFlow's browser-based 'Run Mode' does not execute Custom Actions — they are Dart code that compiles to native, which the web preview cannot run.

Solution: Test Custom Actions using 'Local Run' in FlutterFlow (the device icon at the top), which connects FlutterFlow to an iOS Simulator, Android Emulator, or physical device on the same network. Alternatively, test on a physical device after enabling Developer Mode. This is expected behavior for any FlutterFlow Custom Action — it's not specific to Cloud Functions.

### FirebaseFunctionsException: INTERNAL — function throws an unspecified error

Cause: An unhandled exception inside the Cloud Function is causing it to return a generic INTERNAL error code. The actual error is in the function logs, not visible to the client.

Solution: Open Firebase console → Functions → Logs and filter by your function name. Look for the actual error message — it could be a missing secret, a failed third-party API call (e.g. Stripe rate limit, OpenAI 429), a null pointer, or a JSON parse error. Fix the underlying issue in your function code. Add explicit try/catch in the function with throw new HttpsError('internal', 'Descriptive message') to surface meaningful error codes to FlutterFlow.

```
// Replace generic throws with typed HttpsError
try {
  const result = await externalApiCall();
  return result;
} catch (err) {
  console.error('External API failed:', err);
  throw new HttpsError('unavailable', 'External service temporarily unavailable.');
}
```

## Frequently asked questions

### Do I need the Firebase Blaze plan to use Cloud Functions?

Yes — Cloud Functions (both Firebase Cloud Functions and Google Cloud Functions) require the Blaze (pay-as-you-go) plan. The Spark (free) plan does not allow function deployment. However, Blaze includes a generous free tier: 2 million invocations per month, 400K GB-seconds of compute, and 200K CPU-seconds at no charge. Most FlutterFlow apps in development and early production never exceed this grant. Upgrade to Blaze and immediately set a billing budget alert to protect against unexpected costs.

### What's the difference between a Callable Function and an HTTP trigger for FlutterFlow?

A Callable Function is invoked from Dart code using the Firebase SDK (cloud_functions package) and automatically passes the signed-in user's Firebase Auth token for verification — no manual token management needed. An HTTP trigger is a standard REST endpoint called via FlutterFlow's API Calls panel and requires you to pass and validate auth tokens manually. For user-authenticated operations, Callable Functions are simpler and more secure. For webhooks and integrations with third-party systems that need to call your function, use HTTP triggers.

### Can I use Google Cloud Functions without connecting FlutterFlow to Firebase?

Yes. HTTP-triggered Cloud Functions are callable from FlutterFlow's API Calls panel regardless of Firebase connection — they're just HTTPS endpoints. However, you'll need to implement your own authentication scheme inside the function (API key validation, JWT verification, etc.) since there's no automatic Firebase ID token. The Firebase-native Callable Function approach is only available when the FlutterFlow project is connected to a Firebase project.

### Why does my Custom Action calling a Callable Function work on a device but not in FlutterFlow's Run Mode?

FlutterFlow's browser-based Run Mode (the live preview in the editor) cannot execute Custom Actions written in Dart — they require a compiled native runtime. This is expected behavior, not a bug. To test Callable Function custom actions, use 'Local Run' in FlutterFlow (connects to an iOS Simulator or Android Emulator) or install the app on a physical device via the local run APK/IPA. All other FlutterFlow features work in Run Mode; only Custom Actions need device testing.

### How do I store API keys for my Cloud Function?

Use defineSecret from firebase-functions/params (Gen 2). This stores the secret in Google Secret Manager and injects it into the function at runtime — it's never visible in your source code, Cloud Function configuration UI, or client app. Set secrets using the Firebase CLI: firebase functions:secrets:set MY_SECRET_KEY. Inside the function, reference it as MY_SECRET.value() only within the function handler body, after declaring the secret with defineSecret at the top level.

---

Source: https://www.rapidevelopers.com/flutterflow-integrations/google-cloud-functions
© RapidDev — https://www.rapidevelopers.com/flutterflow-integrations/google-cloud-functions
