Skip to main content
RapidDev - Software Development Agency
flutterflow-tutorials

How to Implement Server-Side Logic in a FlutterFlow App

Add server-side logic to FlutterFlow by writing Firebase Cloud Functions that handle tasks the client app cannot do safely: payment processing, sending email, aggregating statistics, and proxying third-party APIs with secret keys. Use callable functions (onCall) for user-initiated operations — they automatically include authentication. Use HTTP triggers (onRequest) only for webhooks, and always validate incoming requests.

What you'll learn

  • When to use server-side logic in a FlutterFlow app and what tasks must run server-side
  • The difference between HTTP trigger functions (onRequest) and callable functions (onCall) and when to use each
  • How to call a Firebase Cloud Function from a FlutterFlow Action Flow
  • How to use background Firestore triggers for automated server-side processing
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Beginner11 min read30-40 minFlutterFlow Free+ (Firebase Blaze plan required for Cloud Functions)March 2026RapidDev Engineering Team
TL;DR

Add server-side logic to FlutterFlow by writing Firebase Cloud Functions that handle tasks the client app cannot do safely: payment processing, sending email, aggregating statistics, and proxying third-party APIs with secret keys. Use callable functions (onCall) for user-initiated operations — they automatically include authentication. Use HTTP triggers (onRequest) only for webhooks, and always validate incoming requests.

Firebase Cloud Functions as FlutterFlow's Server Layer

FlutterFlow apps run entirely on the client device — the app talks directly to Firestore, Firebase Storage, and your API integrations. This is powerful for most use cases, but some logic must run on a trusted server. Any operation that involves a secret key (Stripe secret key, SendGrid API key, OpenAI API key), sensitive data aggregation, or enforced business rules that users should not be able to bypass must run server-side. Firebase Cloud Functions fill this role for FlutterFlow apps. They are Node.js (or Python) functions that run in Google's infrastructure, triggered by HTTP requests, Firestore document changes, or scheduled cron jobs. This tutorial covers the four most common server-side patterns for FlutterFlow apps.

Prerequisites

  • FlutterFlow project connected to Firebase on the Blaze pay-as-you-go plan (required for Cloud Functions)
  • Node.js installed locally for writing and deploying Cloud Functions
  • Firebase CLI installed: npm install -g firebase-tools
  • Basic understanding of JavaScript or TypeScript for writing function logic

Step-by-step guide

1

Understand when to use server-side logic

Not everything needs to run server-side — move logic to Cloud Functions only when it falls into one of these categories. First, secret key usage: Stripe's secret key, email provider API keys, OpenAI API keys, and any credential that would expose your accounts if it appeared in client code. Second, trusted computations: order totals, subscription checks, user role assignments, and any number the user should not be able to falsify. Third, side effects: sending emails, SMS, push notifications, or writing to multiple Firestore documents atomically using a transaction. Fourth, third-party webhooks: Stripe payment webhooks, Twilio callbacks, and other external service notifications arrive via HTTP POST to your server. Understanding these categories helps you decide exactly which actions stay in FlutterFlow's visual Action Flows and which need a Cloud Function.

Expected result: You have a clear mental model of which operations in your app require Cloud Functions and which can stay client-side.

2

Write and deploy a callable Cloud Function (onCall)

Callable functions (functions.https.onCall) are the preferred way to call Cloud Functions from FlutterFlow because they automatically include the caller's Firebase Authentication token in every request. This means context.auth in your function contains the verified user ID — no need to pass tokens manually or write authentication middleware. Create a function like processPayment that accepts a data object with priceId and calls the Stripe API using your secret key (stored as a Cloud Function environment variable). Return a JSON result. Deploy with firebase deploy --only functions:processPayment. In FlutterFlow, call the function using an API Call action with method POST to your function's callable URL, or use the Firebase Functions integration in FlutterFlow's API Manager.

process_payment.js
1// Cloud Function: processPayment (callable)
2// Receives: { priceId: string }
3// Returns: { clientSecret: string }
4const functions = require('firebase-functions');
5const admin = require('firebase-admin');
6const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
7
8if (!admin.apps.length) admin.initializeApp();
9
10exports.processPayment = functions.https.onCall(async (data, context) => {
11 // Verify the user is authenticated
12 if (!context.auth) {
13 throw new functions.https.HttpsError(
14 'unauthenticated',
15 'Must be signed in to make a payment'
16 );
17 }
18
19 const { priceId } = data;
20 if (!priceId) {
21 throw new functions.https.HttpsError('invalid-argument', 'priceId is required');
22 }
23
24 // Create a PaymentIntent via Stripe
25 const paymentIntent = await stripe.paymentIntents.create({
26 amount: 2999, // $29.99 in cents
27 currency: 'usd',
28 metadata: { userId: context.auth.uid, priceId },
29 });
30
31 return { clientSecret: paymentIntent.client_secret };
32});

Expected result: Deploying the function creates a URL like https://REGION-PROJECT.cloudfunctions.net/processPayment. Calling it from FlutterFlow with a Firebase auth token returns a Stripe client secret.

3

Call the Cloud Function from a FlutterFlow Action Flow

In FlutterFlow, open the API Manager (Settings → API Calls → Add API Call). Set the method to POST and the URL to your Cloud Function's URL. Add a Header: Authorization with value Bearer [current_user_auth_token]. Add the request body as JSON with the required parameters. Add a JSON Body field for data (e.g. {"data": {"priceId": "price_123"}}). Note: Firebase callable functions expect the payload wrapped in a data key. Save the API call and give it a name like CallProcessPayment. In your payment button's Action Flow, add a Backend API Request action using this configured call. After a successful response, read the clientSecret from the response body using FFAppState or a Page State variable and pass it to the Stripe payment sheet.

Expected result: Pressing the Pay button in FlutterFlow calls the Cloud Function with the user's auth token, the function processes the payment, and the client secret is returned to the app.

4

Use Firestore background triggers for automated processing

Firestore triggers fire a Cloud Function automatically when a document is created, updated, or deleted — without any client involvement. This is perfect for automated side effects: send a welcome email when a user document is created, calculate order totals when a cart item document is added, or send a push notification when a chat message document is created. Use functions.firestore.document('orders/{orderId}').onCreate() for create triggers. The function receives the new document's data via change.after.data(). Use the admin SDK to read related documents, perform calculations, and write back results. These triggers run even if the user closes the app immediately after creating the document.

on_order_created.js
1// Cloud Function: onOrderCreated (Firestore trigger)
2// Fires when a new document is created in the orders collection
3const functions = require('firebase-functions');
4const admin = require('firebase-admin');
5
6exports.onOrderCreated = functions.firestore
7 .document('orders/{orderId}')
8 .onCreate(async (snap, context) => {
9 const order = snap.data();
10 const orderId = context.params.orderId;
11
12 // Calculate order total from line items
13 const itemsSnap = await admin.firestore()
14 .collection('orders').doc(orderId)
15 .collection('items').get();
16
17 const total = itemsSnap.docs.reduce(
18 (sum, doc) => sum + (doc.data().price * doc.data().quantity), 0
19 );
20
21 // Write total back to the order document
22 await snap.ref.update({ total, status: 'confirmed' });
23
24 // Send confirmation email (using your email provider)
25 await sendConfirmationEmail(order.userEmail, orderId, total);
26
27 return null;
28 });

Expected result: When FlutterFlow creates an order document in Firestore, the Cloud Function fires automatically within seconds, calculates the total, updates the document, and sends a confirmation email.

5

Secure HTTP trigger functions (onRequest) with authentication

Unlike callable functions, HTTP trigger functions (onRequest) do not automatically verify authentication. They receive a raw HTTP request and can be called by anyone who knows the URL. If you use onRequest for internal app logic (not for public webhooks), always validate the Authorization header manually. Extract the Bearer token from req.headers.authorization, call admin.auth().verifyIdToken(token), and reject requests where verification fails with a 401 response. For third-party webhooks like Stripe, verify the webhook signature using the provider's SDK instead of Firebase auth. Never skip authentication on onRequest functions that perform write operations.

secure_endpoint.js
1// HTTP trigger with authentication validation
2const functions = require('firebase-functions');
3const admin = require('firebase-admin');
4
5exports.secureEndpoint = functions.https.onRequest(async (req, res) => {
6 // Validate Firebase ID token
7 const authHeader = req.headers.authorization || '';
8 if (!authHeader.startsWith('Bearer ')) {
9 return res.status(401).json({ error: 'Unauthorized' });
10 }
11
12 try {
13 const token = authHeader.split('Bearer ')[1];
14 const decoded = await admin.auth().verifyIdToken(token);
15 // decoded.uid is the verified user ID
16 const uid = decoded.uid;
17
18 // Your logic here
19 res.json({ success: true, uid });
20 } catch (err) {
21 return res.status(401).json({ error: 'Invalid token' });
22 }
23});

Expected result: Unauthenticated requests to the function return 401. Only requests with a valid Firebase ID token proceed to the business logic.

Complete working example

on_order_created.js
1// Cloud Function: onOrderCreated
2// Firestore background trigger for the orders collection
3// Calculates order total, updates status, sends confirmation email
4
5const functions = require('firebase-functions');
6const admin = require('firebase-admin');
7
8if (!admin.apps.length) admin.initializeApp();
9
10exports.onOrderCreated = functions.firestore
11 .document('orders/{orderId}')
12 .onCreate(async (snap, context) => {
13 const order = snap.data();
14 const orderId = context.params.orderId;
15 const db = admin.firestore();
16
17 try {
18 // 1. Fetch line items from subcollection
19 const itemsSnap = await db
20 .collection('orders').doc(orderId)
21 .collection('items').get();
22
23 // 2. Calculate total
24 let subtotal = 0;
25 itemsSnap.docs.forEach((doc) => {
26 const item = doc.data();
27 subtotal += (item.price || 0) * (item.quantity || 1);
28 });
29 const tax = Math.round(subtotal * 0.1); // 10% tax
30 const total = subtotal + tax;
31
32 // 3. Update order document with calculated values
33 await snap.ref.update({
34 subtotal,
35 tax,
36 total,
37 status: 'confirmed',
38 confirmedAt: admin.firestore.FieldValue.serverTimestamp(),
39 });
40
41 // 4. Notify the user via Firestore notification document
42 if (order.userId) {
43 await db.collection('notifications').add({
44 userId: order.userId,
45 title: 'Order Confirmed',
46 body: `Your order #${orderId.substring(0, 8)} has been confirmed. Total: $${(total / 100).toFixed(2)}`,
47 type: 'order_confirmed',
48 orderId,
49 read: false,
50 createdAt: admin.firestore.FieldValue.serverTimestamp(),
51 });
52 }
53
54 console.log(`Order ${orderId} processed: total $${total}`);
55 return null;
56 } catch (err) {
57 console.error(`Error processing order ${orderId}:`, err);
58 await snap.ref.update({ status: 'processing_error', error: err.message });
59 return null;
60 }
61 });

Common mistakes

Why it's a problem: Using HTTP trigger functions (onRequest) for app logic without authentication checks

How to avoid: Use callable functions (onCall) for all logic triggered by your FlutterFlow app — they automatically verify Firebase authentication. Only use onRequest for third-party webhooks, and validate the webhook signature using the provider's own verification method.

Why it's a problem: Storing API secret keys as hardcoded strings in Cloud Function files

How to avoid: Store all API keys as Cloud Function environment variables using firebase functions:config:set myservice.key=VALUE. Access them in code via functions.config().myservice.key or process.env.MY_KEY with the newer parameterized configuration.

Why it's a problem: Writing non-idempotent Firestore trigger functions

How to avoid: Use the Firestore event ID (context.eventId) to create a deduplification record at the start of your trigger. Check if the event has already been processed by reading from a processed_events collection — if so, return early without executing the business logic again.

Best practices

  • Use callable functions (onCall) for all app-initiated server logic — they include authentication automatically
  • Store all API keys and secrets in Cloud Functions environment variables, never in function code
  • Make Firestore trigger functions idempotent using the event ID to prevent duplicate processing on retries
  • Keep Cloud Functions small and focused on a single task — large functions are harder to debug and test
  • Always validate the data parameter in callable functions before using it — users can send arbitrary values
  • Set function timeout and memory limits appropriate to the task — payment functions may need 60 seconds
  • Test Cloud Functions locally with firebase emulators:start before deploying to production

Still stuck?

Copy one of these prompts to get a personalized, step-by-step explanation.

ChatGPT Prompt

I am building a FlutterFlow app and need to process Stripe payments server-side using a Firebase Cloud Function. The function should accept a priceId from the app, verify the user is authenticated using Firebase Auth, create a Stripe PaymentIntent using my secret key stored as an environment variable, and return the clientSecret to the app. Write this as a Firebase callable function in Node.js.

FlutterFlow Prompt

Create a FlutterFlow API Call configuration for a Firebase callable Cloud Function named processPayment. The function URL is https://us-central1-myproject.cloudfunctions.net/processPayment. It needs an Authorization header with the current user's Firebase ID token and a JSON body with the format {"data": {"priceId": "PRICE_ID"}}. The response will contain {"result": {"clientSecret": "..."}}.

Frequently asked questions

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

Yes. Firebase Cloud Functions require the Blaze (pay-as-you-go) plan because they run on Google Cloud infrastructure. The Blaze plan includes a generous free tier: 2 million function invocations, 400,000 GB-seconds of compute, and 200GB of outbound data per month. Most small to mid-sized apps will stay within the free tier for Cloud Functions costs.

What is the difference between callable functions (onCall) and HTTP functions (onRequest)?

Callable functions (onCall) automatically include the Firebase Authentication token from the calling device, provide built-in error handling with typed HttpsError, and are designed to be called from Firebase SDKs. HTTP functions (onRequest) are standard HTTP endpoints that can be called by any HTTP client — they are better for webhooks from external services. For FlutterFlow app logic, always prefer onCall.

How do I pass the Firebase ID token from FlutterFlow to a Cloud Function?

Create a Custom Action in FlutterFlow that calls FirebaseAuth.instance.currentUser?.getIdToken(). Store the returned token in an App State variable. In your API Call configuration, add an Authorization header with the value 'Bearer ' + the token App State variable. Tokens expire after 1 hour, so refresh by calling getIdToken(true) which forces a refresh.

Can I use Cloud Functions to send push notifications to other users?

Yes. Use a Firestore trigger on a messages collection — when a new message is created, the trigger looks up the recipient's FCM token from Firestore and sends a push notification via admin.messaging().send(). This is more reliable than client-side notification sending because it works even if the sender's app is closed before the notification is dispatched.

How long does a Cloud Function take to respond?

A warm Cloud Function (recently invoked) typically responds within 50-200ms. A cold start (first invocation after a period of inactivity) can take 1-3 seconds for Node.js functions. To reduce cold starts, keep your function bundle small by avoiding large dependencies, and consider using Cloud Functions minimum instances (Cloud Functions v2) to keep at least one warm instance available.

Can RapidDev write Cloud Functions for my FlutterFlow app?

Yes. RapidDev Engineering Team regularly writes and deploys Cloud Functions for FlutterFlow projects — payment processing, email automation, complex business logic, and API integrations with third-party services. For complex server-side requirements, this is often the fastest path to a working implementation.

RapidDev

Talk to an Expert

Our team has built 600+ apps. Get personalized help with your project.

Book a free consultation

Need help with your project?

Our experts have built 600+ apps and can accelerate your development. Book a free consultation — no strings attached.

Book a free consultation

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.