Skip to content

Commit 799fe91

Browse files
authored
Merge pull request #40 from FlutterFlow/feature/supabase-edge-functions
Add Supabase Edge Functions Docs
2 parents 1036581 + 7dc8537 commit 799fe91

File tree

8 files changed

+349
-8
lines changed

8 files changed

+349
-8
lines changed
78.7 KB
Binary file not shown.
226 KB
Binary file not shown.
172 KB
Binary file not shown.
97.2 KB
Binary file not shown.
67.2 KB
Binary file not shown.
52.3 KB
Binary file not shown.
76 KB
Binary file not shown.

docs/integrations/supabase.md

Lines changed: 349 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ title: Supabase
44
description: Learn how to connect your Dreamflow app with Supabase to enable powerful backend features such as authentication, databases, storage, and more.
55
tags: [Supabase, Integration, Dreamflow, Backend]
66
sidebar_position: 2
7-
toc_max_heading_level: 4
7+
toc_max_heading_level: 3
88
keywords: [Supabase, Integration, Dreamflow, Backend]
99
---
1010

@@ -165,6 +165,246 @@ You can only generate sample data **once** per project. If you need to modify or
165165

166166
:::
167167

168+
## Edge Functions
169+
170+
[Supabase Edge Functions](https://supabase.com/docs/guides/functions) let you run secure, server-side code without needing your own backend. These functions are perfect for tasks that require backend logic, secret handling, or integrations with external APIs such as OpenAI. Because they run on Supabase’s global edge network, they are fast, scalable, and isolated from your client code.
171+
172+
:::info
173+
Unlike database functions or triggers, edge functions execute outside the database engine, giving you full flexibility to write custom logic without being tied to SQL or database-level events.
174+
:::
175+
176+
Edge Functions are ideal for:
177+
178+
- **Calling external APIs securely** (e.g., Stripe, Twilio, OpenAI) without exposing keys in the client app
179+
- **Generating personalized AI content** such as product recommendations, support replies, onboarding guides, weekly usage summaries, or tailored learning suggestions
180+
- **Running scheduled or on-demand automations**
181+
- **Performing complex business logic** that is not suitable for client-side execution
182+
- **Enforcing secure access rules** using Supabase Auth (require logged-in user)
183+
184+
:::info
185+
You can find [**more examples**](https://supabase.com/docs/guides/functions#examples) in the official documentation.
186+
:::
187+
188+
:::warning
189+
Dreamflow doesn’t support running Edge Functions locally yet because the editor has no built-in terminal or local [**Deno**](https://deno.com/) runtime.
190+
:::
191+
192+
### Create and Deploy
193+
194+
Dreamflow provides a built-in workflow to generate, edit, configure, and deploy Supabase Edge Functions directly from your project — no command line needed.
195+
196+
To create and deploy the Edge Function, follow these steps:
197+
198+
#### 1. Create a New Edge Function
199+
200+
1. Open the **Supabase module** from the left sidebar.
201+
2. Scroll to **Edge Functions** and click **+** to create a new one.
202+
:::note
203+
The agent may also determine when parts of your feature implementation should live in the backend and proactively create Edge Function code on your behalf.
204+
:::
205+
3. This opens the **Agent Panel** on the right with a prefilled starter prompt. Simply continue describing what you want your function to do. For example:
206+
207+
```jsx
208+
//Agent Prompt
209+
Create a Supabase Edge Function that uses OpenAI api to generate a motivational message based on the users habit progress.
210+
```
211+
212+
![create-edge-functions.avif](imgs/create-edge-functions.avif)
213+
214+
The agent will create a complete Edge Function using Typescript, including folders and `index.ts`. You will now see the function generated under the following structure:
215+
216+
![edge-function-file.avif](imgs/edge-function-file.avif)
217+
218+
:::info
219+
You can open and edit the function like any other file in Dreamflow.
220+
:::
221+
222+
#### 2. Add Required Secrets
223+
224+
If your Edge Functions require secrets like API keys, Dreamflow automatically detects them from your generated code and allows you to add the required values.
225+
226+
To configure your function’s secrets, open the **Supabase > Secrets** section in Dreamflow and add the values specific to your edge function.
227+
228+
Saving these values in Dreamflow pushes them to your Supabase project.
229+
230+
![add-secrect.avif](imgs/add-secrect.avif)
231+
232+
#### 3. Deploy the Function
233+
234+
After reviewing your function and adding secrets you are ready to deploy your function.
235+
236+
1. Open the **Supabase > Edge Functions** section.
237+
2. Click **Deploy Function**.
238+
3. A confirmation dialog appears; click **Deploy**.
239+
240+
![deploy-edge-funciton.avif](imgs/deploy-edge-funciton.avif)
241+
242+
243+
#### 4. Verify Deployment
244+
245+
After deployment, you can open the **Supabase Dashboard > Edge Functions** page to ensure your function has been properly deployed.
246+
247+
![sb-edge-functions.avif](imgs/sb-edge-functions.avif)
248+
249+
250+
#### 5. Run or Test Your Edge Function
251+
252+
Once your Edge Function is deployed, you can trigger it directly from your app.
253+
254+
**Using Agent**
255+
256+
You can ask the Agent to connect the function to the correct part of your UI. For example:
257+
258+
```jsx
259+
Call the `generate-motivation` Supabase Edge Function on the home page and display the returned message in the motivational message widget.
260+
```
261+
262+
:::note
263+
The agent should also automatially wire up the function to the correct parts of your app as you build.
264+
:::
265+
266+
The agent will automatically place the call in the appropriate widget, manage loading states, and update your UI.
267+
268+
**Add Manually**
269+
270+
If you prefer to call the function manually, here is the recommended method using the official [**supabase_flutter**](https://pub.dev/packages/supabase_flutter) package. Here’s an example code:
271+
272+
```jsx
273+
import 'package:supabase_flutter/supabase_flutter.dart';
274+
import 'dart:convert';
275+
276+
Future<String> callGenerateMotivation() async {
277+
final supabase = Supabase.instance.client;
278+
279+
final response = await supabase.functions.invoke(
280+
'generate-motivation',
281+
body: {
282+
'progress': 0.6,
283+
'completedHabits': 3,
284+
'totalHabits': 5,
285+
'userName': 'Mike',
286+
},
287+
);
288+
289+
if (response.data != null) {
290+
return response.data['message'] ?? 'No message returned.';
291+
} else {
292+
throw Exception('Failed: ${response.error?.message}');
293+
}
294+
}
295+
```
296+
297+
Then, you can call `callGenerateMotivation()` from anywhere in your app, such as:
298+
299+
```jsx
300+
ElevatedButton(
301+
onPressed: () async {
302+
final message = await callGenerateMotivation();
303+
print(message);
304+
},
305+
child: Text(message),
306+
)
307+
```
308+
309+
:::tip[Monitor Your Edge Function]
310+
311+
After deploying your Edge Function, you can view detailed insights directly from the **Supabase Dashboard**. Open your project in Supabase > **Edge Functions** > select your function. From there, you can:
312+
313+
- **View Invocations** (every time your app calls the function)
314+
- **Check Logs** for debugging and server output
315+
- **Function Code** to update and deploy code directly
316+
- **Deployment Details**
317+
318+
![sb-edge-functions-logs](imgs/sb-edge-functions-logs.avif)
319+
320+
This is extremely helpful for verifying that your function is running correctly and diagnosing any issues.
321+
322+
:::
323+
324+
### Type Generation
325+
326+
Dreamflow can generate fully typed TypeScript definitions based on your database schema. These types ensure your Edge Functions stay in sync with your tables, columns, and relationships, providing safer queries and better autocomplete during development.
327+
328+
For example, without types you might accidentally write:
329+
330+
```tsx
331+
await supabase.from("profiles").insert({ fullName: "Mike" });
332+
// ❌ Bug: "fullName" is not a column in the profiles table
333+
```
334+
335+
This won’t fail until the app is actually running.
336+
337+
With generated types, TypeScript immediately catches the mistake:
338+
339+
```tsx
340+
// ❌ TypeScript error: "fullName" does not exist on type Insert<profiles>
341+
```
342+
343+
Instead of guessing column names or discovering mistakes only at runtime, you get immediate feedback and accurate autocomplete based on your real schema.
344+
345+
#### Generate Types from Database Schema
346+
347+
You can easily generate the `database.types.ts` file by asking the Dreamflow agent.
348+
349+
Use this Agent prompt:
350+
351+
```jsx
352+
Generate a `database.types.ts` file for my connected Supabase project based on the current database schema.
353+
```
354+
355+
:::warning
356+
Types do not update automatically. You must regenerate them whenever your database schema changes.
357+
:::
358+
359+
#### Use Generated Types
360+
361+
Inside any Edge Function, you can import the generated types:
362+
363+
```tsx
364+
import { Database } from "../../database.types.ts";
365+
```
366+
367+
Then you can use them like this:
368+
369+
**Example: Type-safe Table Insert**
370+
371+
```tsx
372+
type ProfileInsert = Database["public"]["Tables"]["profiles"]["Insert"];
373+
374+
await supabase.from("profiles").insert<ProfileInsert>({
375+
id: userId,
376+
display_name: name,
377+
});
378+
```
379+
380+
**Example: Strongly-typed Row**
381+
382+
```tsx
383+
type ProfileRow = Database["public"]["Tables"]["profiles"]["Row"];
384+
385+
const { data } = await supabase.from("profiles").select("*").eq("id", userId);
386+
387+
const profile: ProfileRow = data[0]; // typed and validated
388+
```
389+
390+
391+
### Best Practices
392+
393+
- **Each Function Lives in Its Own Folder**: Every edge function should have its own directory containing an `index.ts` entry file. For example:
394+
395+
```jsx
396+
supabase/
397+
functions/
398+
generate-summary/
399+
index.ts
400+
send-email/
401+
index.ts
402+
```
403+
404+
- **Use Shared Modules for Reusable Logic:** If multiple functions need the same utilities (like an OpenAI client, validators, or formatting helpers), place them inside `supabase/functions/_shared/`. This avoids code duplication and keeps each function focused only on its own task.
405+
- **Use `database.types.ts` for Strong Typing:** Generate and import `database.types.ts` to ensure all database queries inside edge functions match your actual schema. This provides autocomplete, prevents schema mismatches, and makes your functions safer and easier to maintain.
406+
- **Use Consistent Error Handling Patterns:** Implement clear and predictable error handling inside each Edge Function. Wrap risky operations in `try/catch`, log errors using `console.error()` so they appear in the Supabase Dashboard logs, and always return structured JSON error responses.
407+
168408
## FAQs
169409
170410
<details>
@@ -179,28 +419,129 @@ To fix this, use a valid email address during sign-up so you can receive and con
179419
</p>
180420
</details>
181421
422+
<details>
423+
<summary>
424+
Why can’t I log in with the email I used to generate sample data?
425+
</summary>
426+
427+
<p>
428+
If you generated sample data before adding authentication to your app, logging in with that same email will fail — and this is expected behavior.
429+
430+
When sample data is created, it inserts records directly into the Supabase database, including user details, but it doesn’t go through the actual authentication process. As a result, those users exist in the database but don’t have valid authentication credentials in Supabase Auth.
431+
432+
To fix it, you can delete the dummy user record from the Auth table and then sign up again in your app with that email.
433+
</p>
434+
</details>
435+
436+
<details>
437+
<summary>
438+
Why do I get “ClientException: Failed to fetch” when calling an Edge Function?
439+
</summary>
440+
441+
<p>
442+
This error almost always happens because the **browser blocked the request due to CORS**, so your Supabase Edge Function never received it. The browser sends a **CORS preflight (OPTIONS)** request first. If your function doesn’t handle `OPTIONS` or doesn’t return valid `Access-Control-Allow-*` headers, the browser stops the request and it reports “Failed to fetch.”
443+
444+
**Fix:** Add proper CORS handling inside your Edge Function. The example below includes the required CORS configuration—most importantly the *Access-Control-Allow-Origin* header—along with the other *Access-Control-Allow-** headers and correct handling of the OPTIONS preflight request.
445+
446+
```jsx
447+
// CORS headers added to allow the browser request to pass
448+
// The key fix is "Access-Control-Allow-Origin": "*"
449+
const corsHeaders = {
450+
"Access-Control-Allow-Origin": "*", // <-- required for web requests
451+
"Access-Control-Allow-Headers":
452+
"authorization, x-client-info, apikey, content-type", // required for Supabase auth + JSON
453+
"Access-Control-Allow-Methods": "GET, POST, OPTIONS", // allow methods including OPTIONS
454+
};
455+
456+
Deno.serve(async (req) => {
457+
if (req.method === "OPTIONS") {
458+
return new Response("ok", { headers: corsHeaders });
459+
}
460+
461+
const url = new URL(req.url);
462+
const habit = url.searchParams.get("habit") ?? "daily";
463+
const tone = url.searchParams.get("tone") ?? "encouraging";
464+
465+
return new Response(JSON.stringify({
466+
message: `Tiny step, big impact. Your ${habit} practice is blooming — keep it ${tone}!`,
467+
}), {
468+
headers: { ...corsHeaders, "Content-Type": "application/json" },
469+
});
470+
});
471+
472+
```
473+
474+
After updating, redeploy your edge function and test it. Then open **Supabase Dashboard → Edge Functions → Logs** to verify that the request is now reaching the server.
475+
</p>
476+
</details>
477+
182478
183479
184480
<details>
185481
<summary>
186-
Are Supabase Edge Functions supported in Dreamflow?
482+
Do I need to be logged in when calling my Supabase Edge Function?
187483
</summary>
188484
189485
<p>
190-
Currently, **Edge Functions are not supported in Dreamflow**. If you need to create or manage Edge Functions, you’ll have to do so directly from the **Supabase Console**.
486+
It depends on whether the function **requires JWT verification**. If the “Verify JWT with legacy secret” toggle is enabled in your Supabase Dashboard, the function expects an authenticated request with a valid JWT in the `Authorization` header. In that case, you must be logged in before calling the function; otherwise the server will reject the request with a **401 Unauthorized** (visible in function logs).
487+
488+
![supabase-edge-function-logged-in](imgs/supabase-edge-function-logged-in.avif)
191489
</p>
192490
</details>
193491
194492
<details>
195493
<summary>
196-
Why can’t I log in with the email I used to generate sample data?
494+
How do I access the logged-in user inside an Edge Function?
197495
</summary>
198496
199497
<p>
200-
If you generated sample data before adding authentication to your app, logging in with that same email will fail — and this is expected behavior.
498+
When your app makes a request and the user is logged in, the user’s JWT is automatically included in the `Authorization` header. Inside your Edge Function, you can read this header and fetch the authenticated user like this:
201499
202-
When sample data is created, it inserts records directly into the Supabase database, including user details, but it doesn’t go through the actual authentication process. As a result, those users exist in the database but don’t have valid authentication credentials in Supabase Auth.
500+
```tsx
501+
const authHeader = req.headers.get("Authorization");
502+
const { data: user } = await supabase.auth.getUser(authHeader);
503+
```
203504
204-
To fix it, you can delete the dummy user record from the Auth table and then sign up again in your app with that email.
205-
</p>
505+
The `user` will contain the full Supabase Auth user object. If the request has no token or an invalid token, `user` will be `null`.
506+
</p>
507+
</details>
508+
509+
<details>
510+
<summary>
511+
Why am I getting “Error: Function invocation timed out” and how do I fix it?
512+
</summary>
513+
514+
<p>
515+
516+
This error appears when your Edge Function takes too long to finish, often because it’s waiting on a slow external API call without a timeout. For example, a request like `await fetch("https://slow-api.example.com/report")` can hang indefinitely, causing the function to hit Supabase’s execution limit and fail.
517+
518+
To fix this, add a timeout using `AbortController`:
519+
520+
```tsx
521+
const controller = new AbortController();
522+
const timeout = setTimeout(() => controller.abort(), 5000); // 5s timeout
523+
524+
const response = await fetch("https://slow-api.example.com/report", {
525+
signal: controller.signal,
526+
}).finally(() => clearTimeout(timeout));
527+
```
528+
529+
Then return a clear error when the timeout occurs:
530+
531+
```tsx
532+
try {
533+
// fetch with timeout
534+
} catch (err) {
535+
if (err.name === "AbortError") {
536+
return new Response(
537+
JSON.stringify({ error: "Upstream service timed out" }),
538+
{ status: 504, headers: { "Content-Type": "application/json" } }
539+
);
540+
}
541+
}
542+
```
543+
544+
This prevents your function from hanging, ensures it fails cleanly, and avoids timeout errors in production.
545+
546+
</p>
206547
</details>

0 commit comments

Comments
 (0)