Skip to main content
RapidDev - Software Development Agency
bubble-integrationsBubble API Connector

PostgreSQL

Bubble cannot open a TCP connection to PostgreSQL port 5432 — all database calls must go through HTTPS. The standard path is Supabase, which wraps your PostgreSQL database in an auto-generated REST API (PostgREST) accessible from Bubble's API Connector using your project's anon key. Row Level Security on the Supabase side enforces per-user data access, replacing the need for Bubble privacy rules alone.

What you'll learn

  • Why Bubble cannot connect directly to PostgreSQL and what the correct pattern is
  • How to set up a Supabase project and locate the REST API base URL and anon key
  • How to configure Bubble's API Connector with Supabase PostgREST shared headers
  • How to write PostgREST filter syntax for querying, filtering, inserting, and updating rows
  • How Row Level Security on Supabase protects your data at the database layer
  • How to initialize API Connector calls correctly so Bubble detects the response schema
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Intermediate18 min read1–2 hoursDatabase & BackendLast updated July 2026RapidDev Engineering Team
TL;DR

Bubble cannot open a TCP connection to PostgreSQL port 5432 — all database calls must go through HTTPS. The standard path is Supabase, which wraps your PostgreSQL database in an auto-generated REST API (PostgREST) accessible from Bubble's API Connector using your project's anon key. Row Level Security on the Supabase side enforces per-user data access, replacing the need for Bubble privacy rules alone.

Quick facts about this guide
FactValue
ToolPostgreSQL
CategoryDatabase & Backend
MethodBubble API Connector
DifficultyIntermediate
Time required1–2 hours
Last updatedJuly 2026

Bubble + PostgreSQL: Why 'Direct Connection' Is Impossible and What to Do Instead

When Bubble founders search for 'bubble postgresql integration,' many tutorials claim you can 'connect directly' to PostgreSQL. This is misleading. Bubble is a browser-based application builder — its workflows run in users' browsers, not on a dedicated server with network access to database ports. PostgreSQL listens on TCP port 5432; no browser can open a raw TCP socket to an external database server, and Bubble's API Connector only supports HTTPS calls.

The solution is a REST layer in front of PostgreSQL that converts HTTP requests into database queries and returns JSON responses. Supabase is by far the most common choice for Bubble founders, and for good reason: when you create a table in Supabase, PostgREST (a component built into Supabase) automatically generates REST endpoints for that table with no code required. The endpoint `/rest/v1/your_table` immediately supports GET (read), POST (insert), PATCH (update), and DELETE operations with PostgreSQL's full filtering and sorting capabilities via query parameters.

The auth model in Supabase-Bubble integrations is where many founders make mistakes. Supabase provides two keys: - The **anon key** is safe to put in Bubble's API Connector shared headers (not even marked Private, though marking it Private is still good practice). Row Level Security (RLS) policies enforce what the anon key can access — if a user is anonymous (no JWT), they only see rows that public RLS policies allow. If a user is authenticated (passes a JWT in the Authorization header), they see rows their user-specific RLS policies allow. - The **service_role key** bypasses all RLS policies. It must NEVER be used in client-side Bubble workflows. Only use it in Backend Workflows with a Private header, and only when you genuinely need admin-level database access.

For Bubble apps where most data operations are tied to a logged-in user, the recommended pattern is: client-side reads use the anon key with RLS enforcing per-user access; writes use Backend Workflows with the service_role key (paid plan only) when you need to guarantee writes go through correctly regardless of auth state.

Founders with an existing self-hosted PostgreSQL instance (not on Supabase) can achieve the same pattern by deploying PostgREST self-hosted, using Hasura, or writing a simple REST API in Express or FastAPI in front of their database. The principle is the same: Bubble calls HTTPS, the REST layer handles PostgreSQL.

Integration method

Bubble API Connector

Bubble API Connector calls Supabase's PostgREST REST API (base URL: https://{project-id}.supabase.co/rest/v1) with anon key in shared headers; Row Level Security on Supabase enforces per-user access control server-side.

Prerequisites

  • A Bubble app (free or paid; reads via the API Connector work on the free plan; Backend Workflows for admin writes require a paid plan)
  • The Bubble API Connector plugin installed (Plugins tab → Add plugins → search 'API Connector' by Bubble)
  • A Supabase account at supabase.com (free tier includes 2 projects and 500 MB database)
  • Basic understanding of SQL tables and columns, or willingness to use Supabase's Table Editor GUI

Step-by-step guide

1

Create a Supabase project and locate your API credentials

Go to supabase.com → New project. Give it a name, choose the database region closest to your users (this affects API latency for your Bubble users), and set a strong database password (store it somewhere safe — you'll need it for direct database access if debugging). Click 'Create new project' and wait for provisioning (about 2 minutes). Once the project is ready, go to Project Settings (gear icon in the left sidebar) → API. You'll find two pieces of information you need: 1. **Project URL**: `https://{project-id}.supabase.co` — this is the base URL for all API calls 2. **anon key**: a long JWT string labeled 'anon public' — this is what Bubble will use in the Authorization header for client-side calls Also note the **service_role key** — labeled 'service_role secret.' This key bypasses all Row Level Security. Write it down, but do NOT put it in Bubble's API Connector unless it's in a Backend Workflow Private header. Never use it in client-side workflows. The PostgREST REST API base URL is: `https://{project-id}.supabase.co/rest/v1` You'll be pointing Bubble's API Connector at this base URL with two shared headers: `apikey: {anon_key}` and `Authorization: Bearer {anon_key}`. Both headers use the same anon key — this is the correct setup for client-safe reads.

supabase-api-config.json
1{
2 "base_url": "https://your-project-id.supabase.co/rest/v1",
3 "headers": {
4 "apikey": "your-anon-key",
5 "Authorization": "Bearer your-anon-key"
6 }
7}

Pro tip: The project ID is the subdomain in your Project URL — the string between 'https://' and '.supabase.co'. It's also visible in the Supabase dashboard URL when you're inside the project settings.

Expected result: You have your Supabase Project URL, anon key, and service_role key. The PostgREST base URL is noted as https://{project-id}.supabase.co/rest/v1.

2

Create tables and enable Row Level Security

In Supabase, go to Table Editor → New table. For a typical Bubble app user-data table, create a table called `profiles` with these columns: - `id` (uuid, primary key, default: `gen_random_uuid()`) - `user_id` (uuid, references `auth.users.id` if using Supabase Auth, or just a text field storing Bubble user IDs) - `display_name` (text) - `created_at` (timestamptz, default: `now()`) For tables storing user-specific data, immediately enable Row Level Security. In the table editor, click on the table → RLS → Enable RLS. Then add policies: **Policy for user-specific reads (if using Supabase Auth)**: - Policy name: `Users read own rows` - Command: SELECT - USING expression: `auth.uid() = user_id` **Policy for open reads (public content)**: - Policy name: `Anyone can read` - Command: SELECT - USING expression: `true` For Bubble apps that use Bubble's own auth (not Supabase Auth), you won't have Firebase-style UIDs in the JWT. In this case, either use a Backend Workflow with the service_role key for all writes (the anon key with open SELECT rules for reads), or implement a custom JWT strategy where Bubble generates tokens that Supabase can verify. Insert one test row into your table immediately — Bubble's API Connector 'Initialize call' needs a real response with data to detect the response schema. An empty table returns an empty array, and Bubble cannot detect field names from an empty array.

rls-policies.sql
1-- Enable RLS
2ALTER TABLE profiles ENABLE ROW LEVEL SECURITY;
3
4-- Policy: users can read their own rows
5CREATE POLICY "Users read own rows" ON profiles
6 FOR SELECT USING (auth.uid() = user_id);
7
8-- Policy: users can insert their own rows
9CREATE POLICY "Users insert own rows" ON profiles
10 FOR INSERT WITH CHECK (auth.uid() = user_id);
11
12-- Policy: users can update their own rows
13CREATE POLICY "Users update own rows" ON profiles
14 FOR UPDATE USING (auth.uid() = user_id);

Pro tip: Run RLS policy SQL in the Supabase SQL Editor (left sidebar → SQL Editor → New query). The Table Editor GUI can also create policies, but the SQL Editor gives you more control over the USING and WITH CHECK expressions.

Expected result: Your Supabase table is created, RLS is enabled, and policies are in place. One test row is inserted. The table appears in Supabase Table Editor with data visible in the grid.

3

Configure Bubble API Connector with Supabase PostgREST

In your Bubble app, go to Plugins tab → Add plugins → search 'API Connector' → Install (it's the official plugin by Bubble, free). Once installed, click 'API Connector' in the plugins list to open its configuration panel. Click 'Add another API'. Configure the group: - **API Name**: `Supabase` - **Authentication**: None (auth is handled via shared headers) - **Shared headers**: - `apikey`: `{your-anon-key}` (DO NOT mark as Private — this is an intentionally client-visible key when RLS is properly configured) - `Authorization`: `Bearer {your-anon-key}` (same value) - `Content-Type`: `application/json` The shared header configuration looks like: ```json { "method": "GET", "url": "https://your-project-id.supabase.co/rest/v1/profiles", "headers": { "apikey": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", "Authorization": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", "Content-Type": "application/json" } } ``` Now add your first API call. Click 'Add another call' under the Supabase group: - **Name**: `Get Profiles` - **Method**: GET - **URL**: `/profiles` (PostgREST appends this to the base URL) - Add a query parameter: `select` = `id,user_id,display_name,created_at` (comma-separated column names to return) - **Use as**: Data (for reads that populate repeating groups) For filtering by a specific field, add a query parameter using PostgREST's filter syntax. For example, to filter by user_id: add parameter `user_id` = `eq.{user_id}` where `{user_id}` is a dynamic value you'll pass from Bubble workflows.

get-profiles-call.json
1{
2 "method": "GET",
3 "url": "https://your-project-id.supabase.co/rest/v1/profiles",
4 "headers": {
5 "apikey": "<your-anon-key>",
6 "Authorization": "Bearer <your-anon-key>",
7 "Content-Type": "application/json"
8 },
9 "params": {
10 "select": "id,user_id,display_name,created_at",
11 "user_id": "eq.<dynamic>"
12 }
13}

Pro tip: PostgREST filter syntax uses operator suffixes on the column name: `eq.` (equals), `neq.` (not equals), `lt.` / `gt.` (less/greater than), `ilike.` (case-insensitive text match with `*` wildcards), `in.` (value in list: `in.(1,2,3)`). These go in the query parameter name, with the value as the parameter value.

Expected result: The Supabase API group is created with shared headers. The 'Get Profiles' call appears in the API Connector with GET method and the correct PostgREST URL structure.

4

Initialize the API call to detect response schema

The Bubble API Connector's 'Initialize call' step is required before you can use an API call in workflows. It makes a real HTTPS request to the API and uses the response to detect field names and data types that Bubble will display in workflow action pickers. In the 'Get Profiles' call, click 'Initialize call'. If you have a dynamic parameter like `user_id`, enter a test value matching a real row you created in Step 2 (e.g. if your test row has `user_id = 'test-user-123'`, enter that). Click 'Run'. If the call succeeds with data, Bubble will display the detected fields: `id` (text), `user_id` (text), `display_name` (text), `created_at` (text). Click 'Save'. Common Initialize call problems: - **Empty array returned**: Your test filter parameter matched no rows. Either the table is empty, the filter value doesn't match any rows, or RLS is blocking the query. Insert a test row in Supabase and try again. - **'There was an issue setting up your call'**: The request returned a non-200 status. Check the exact error in Bubble's Logs tab → API connector calls. A 404 usually means the table name is misspelled. A 401/403 usually means the apikey header is missing or incorrect. - **'relation does not exist'**: The table name in the URL doesn't match a table in your Supabase database. Check the exact table name (case-sensitive) in Supabase Table Editor. After initializing the read call, add a POST (insert) call: - **Name**: `Create Profile` - **Method**: POST - **URL**: `/profiles` - **Body**: JSON with dynamic fields: `{"display_name": "{{display_name}}", "user_id": "{{user_id}}"}` - Add header at call level: `Prefer: return=representation` (tells PostgREST to return the created row) - **Use as**: Action (triggered by workflow, not data binding) Initialize the POST call with test values to confirm Bubble detects the returned row schema.

create-profile-call.json
1{
2 "method": "POST",
3 "url": "https://your-project-id.supabase.co/rest/v1/profiles",
4 "headers": {
5 "apikey": "<your-anon-key>",
6 "Authorization": "Bearer <your-anon-key>",
7 "Content-Type": "application/json",
8 "Prefer": "return=representation"
9 },
10 "body": {
11 "display_name": "<dynamic>",
12 "user_id": "<dynamic>"
13 }
14}

Pro tip: If the Initialize call for your GET request fails because the table is empty, add a dummy row in Supabase SQL Editor: `INSERT INTO profiles (display_name, user_id) VALUES ('Test User', 'test-123');` — then initialize, then delete the dummy row. Bubble only needs the schema detection to happen once; it doesn't re-fetch on every workflow run.

Expected result: Both 'Get Profiles' (GET) and 'Create Profile' (POST) calls are initialized. Bubble has detected the field names and types for both. The calls appear in the API Connector with a green checkmark indicating successful initialization.

5

Build Bubble workflows using the Supabase API calls

With initialized API calls, you can now use them across Bubble's workflow editor and data bindings. **Binding a read call to a Repeating Group**: On a page with a Repeating Group, set the Data source to: Plugins → Supabase Get Profiles → result list. The repeating group will render one row per profile returned by the API. Use the repeating group cell's `current cell's Get Profiles` dynamic value to access fields like `current cell's Get Profiles profile's display_name`. **Triggering a write from a button**: Add a workflow triggered by a button's 'click' event. Add action: Plugins → API Connector → Create Profile. Map the parameters: `display_name = Input Display Name's value`, `user_id = Current User's unique id`. After the API call action completes, add 'Display data' to refresh the repeating group. **Using the PATCH method for updates**: Add another API call in the Connector: - Name: `Update Profile` - Method: PATCH - URL: `/profiles?id=eq.{{id}}` - Body: `{"display_name": "{{display_name}}"}` - Prefer header: `return=representation` The `?id=eq.{{id}}` in the URL path (not the body) tells PostgREST which row to update. Pass the row's UUID as the dynamic `id` parameter. **Adding PATCH + DELETE calls**: Follow the same pattern — URL filter on the primary key, body with fields to update (PATCH) or no body (DELETE with URL filter). WU note: each API Connector call made from a client-side Bubble workflow consumes approximately 0.1–0.5 WUs. For apps with many simultaneous users making frequent reads, consider caching Supabase data in Bubble's native database (updated by a scheduled Backend Workflow) to reduce per-user API call WU consumption.

update-profile-call.json
1{
2 "method": "PATCH",
3 "url": "https://your-project-id.supabase.co/rest/v1/profiles?id=eq.<dynamic-id>",
4 "headers": {
5 "apikey": "<your-anon-key>",
6 "Authorization": "Bearer <your-anon-key>",
7 "Content-Type": "application/json",
8 "Prefer": "return=representation"
9 },
10 "body": {
11 "display_name": "<dynamic>"
12 }
13}

Pro tip: PostgREST returns an array even for single-row queries. When you expect exactly one row (e.g. fetching a user's profile by their ID), add header `Accept: application/vnd.pgrst.object+json` to get a single object instead of a one-item array. But beware: PostgREST returns 406 if this header is used and the query returns 0 rows or multiple rows. Handle the empty state in your Bubble workflow first.

Expected result: A Bubble Repeating Group displays profiles fetched from Supabase. A button workflow successfully creates a new profile row. The Supabase Table Editor confirms new rows are appearing with correct data.

6

Add Bubble privacy rules and verify RLS protection

With Supabase RLS enforcing data access at the database layer, your data is protected at the API level. But if you also store references to Supabase records in Bubble's native database (e.g. storing a Supabase row's UUID alongside a Bubble User), add Bubble privacy rules to protect those native data types too. In Bubble, go to Data tab → Data types → click any type that stores Supabase-sourced data → Privacy tab. Add a rule: `When Current User is the creator → Allow find in searches: Yes, Allow reading all fields: Yes`. This ensures even if someone queries your Bubble app's database directly, they can't read other users' records. To verify RLS is working correctly, test with a different user account or an unauthenticated session: 1. Open your Bubble app in an incognito window (unauthenticated) 2. Note what data the Supabase API calls return — with RLS enabled and a `where auth.uid() = user_id` policy, unauthenticated calls should return empty arrays 3. Sign in as a test user and confirm only that user's rows appear If you're using the service_role key anywhere for admin operations, those calls must go through a Bubble Backend Workflow with the key in a Private header. To add admin operations: - In the API Connector, add a second Supabase API group named 'Supabase Admin' - Shared header: `Authorization: Bearer {service_role_key}` marked **Private** - Only use this group's calls inside Backend Workflows - Trigger those Backend Workflows from client-side events using Bubble's 'Schedule API workflow' action RapidDev's team has built dozens of Bubble apps with Supabase backends and complex RLS policies — if you're running into data access issues, book a free scoping call at rapidevelopers.com/contact.

Pro tip: Free Supabase projects pause after a week of inactivity. The first API call after a pause takes 10–20 seconds to respond as the project wakes up. For production apps, upgrade to Supabase Pro ($25/month) to disable pausing, or add a 'health check' API Connector call to your Bubble app's index page load that wakes the project before users interact with it.

Expected result: Bubble privacy rules are configured on any data types that reference Supabase data. RLS verification confirms unauthenticated requests return empty data and authenticated requests return only the current user's rows. The integration is production-ready.

Common use cases

User-specific data application

Build a Bubble app where each logged-in user sees only their own records — tasks, orders, projects, health logs — stored in a Supabase PostgreSQL table with Row Level Security enforcing `auth.uid() = user_id`. Bubble's API Connector reads the data using the user's JWT in the Authorization header, and PostgREST filters results to that user's rows automatically.

Bubble Prompt

Build a Bubble workflow that, on page load, calls the Supabase PostgREST endpoint GET /tasks with the current user's JWT in the Authorization header and displays the returned array in a repeating group sorted by created_at descending.

Copy this prompt to try it in Bubble

Product catalog or content database

Store a product catalog, article library, or FAQ database in Supabase and serve it to a Bubble frontend via PostgREST. PostgreSQL's full-text search, JSONB columns, and composite indexes make it far more capable than Bubble's native database for large catalogs with complex filtering needs.

Bubble Prompt

Create a Supabase table named 'products' with columns id, name, price, category, and description. In Bubble, add a search input that calls GET /products?category=eq.Electronics&name=ilike.*{searchTerm}* and displays results in a repeating group.

Copy this prompt to try it in Bubble

Cross-platform data layer

Use Supabase as the single database backing both your Bubble web app and a separate mobile app or API service — so data created in one platform appears immediately in the other. PostgreSQL's ACID compliance and PostgREST's real-time capabilities via Supabase Realtime make it a reliable shared source of truth.

Bubble Prompt

Set up a Supabase table 'orders' shared between a Bubble admin portal and a React Native mobile app. Bubble writes new orders via POST /orders in a Backend Workflow; the mobile app subscribes to Supabase Realtime for live updates.

Copy this prompt to try it in Bubble

Troubleshooting

API Connector Initialize call fails with 'There was an issue setting up your call' and the table returns a 404

Cause: The table name in the PostgREST URL is misspelled or uses a different case than the actual table name in Supabase. Table names are case-sensitive in PostgREST.

Solution: Go to Supabase Table Editor and confirm the exact table name (including underscores, hyphens, and case). Update the URL in the Bubble API Connector call. Note that Supabase's Table Editor lowercases table names by default — 'Profiles' becomes 'profiles' in the API path.

Initialize call returns an empty array and Bubble cannot detect any fields

Cause: The table has no rows, or the filter parameters in the Initialize call matched zero rows. Bubble cannot infer the response schema from an empty array.

Solution: Insert at least one test row into the Supabase table via the Table Editor or SQL Editor: INSERT INTO your_table (column1, column2) VALUES ('test_value_1', 'test_value_2'); Then re-run the Initialize call. After initialization succeeds, the test row can be deleted — Bubble retains the detected schema.

Supabase returns 401 or 403 on API Connector calls

Cause: The apikey header is missing, incorrect, or the anon key doesn't match the project. Alternatively, RLS policies are blocking the request for an unauthenticated anon key.

Solution: Verify the anon key in Bubble's API Connector matches exactly what's shown in Supabase Project Settings → API → anon key. Check that both the `apikey` and `Authorization: Bearer` shared headers are set to the anon key value. In the Supabase logs (Logs → API), filter for 401/403 requests to see which policy is rejecting the call.

PostgREST returns 406 Not Acceptable when fetching a single row

Cause: You are sending the `Accept: application/vnd.pgrst.object+json` header (for single-object response) but the query returns 0 rows or more than 1 row. PostgREST returns 406 in these cases instead of an empty object.

Solution: Add a Bubble workflow condition before the single-row API call: only proceed if the expected record exists (e.g. check for a non-empty ID). Handle empty states separately. Alternatively, use standard array responses and access the first item in Bubble with 'first item of list' operator, avoiding the single-row Accept header entirely.

Supabase project API calls time out after a period of inactivity

Cause: Free Supabase projects pause automatically after approximately one week without any database activity. The first request after pausing triggers a cold start that can take 10–20 seconds.

Solution: For production apps, upgrade to Supabase Pro ($25/month) to disable automatic pausing. For development or low-traffic apps on the free tier, add a lightweight 'ping' API call (e.g. GET /rest/v1/your_table?select=id&limit=1) on your Bubble app's index page load event to wake the project proactively before users interact with real data.

Best practices

  • Always enable Row Level Security on every Supabase table before writing any real user data. A table with RLS disabled is publicly readable and writable by anyone with your anon key — the PostgREST API exposes it without restriction.
  • Use the anon key for client-side reads and the service_role key only in Backend Workflow Private headers for admin writes. Never expose the service_role key in client-side Bubble workflows — it bypasses all RLS and gives full database access.
  • Insert at least one test row in each Supabase table before running the Bubble API Connector Initialize call. Bubble cannot detect field names from empty arrays. Delete the test row after initialization completes.
  • Use PostgREST's server-side filtering (`?column=eq.value`) instead of fetching all rows and filtering in Bubble's workflow logic. Server-side filtering reduces data transfer, speeds up your app, and reduces WU consumption.
  • For apps with many simultaneous users, cache frequently-read Supabase data in Bubble's native database, updated by a scheduled Backend Workflow every few minutes. This avoids N×API calls per page load where N is the number of active users.
  • Add Bubble privacy rules on any native data type that stores Supabase-sourced data or references. RLS protects the Supabase database; Bubble privacy rules protect the Bubble database — both layers matter.
  • Upgrade to Supabase Pro ($25/month) for production apps to disable automatic project pausing, get daily backups, and access the full connection pool. Free tier pausing causes 10–20 second delays for returning users.
  • Use the `Prefer: return=representation` header on POST and PATCH calls to get the created/updated row back in the response. Without this header, Supabase returns HTTP 201/204 with no body, and Bubble cannot access the new row's auto-generated ID.

Alternatives

Frequently asked questions

Can I connect Bubble directly to a PostgreSQL database without Supabase?

Not directly — Bubble cannot open TCP connections to port 5432. You need an HTTPS REST layer in front of PostgreSQL. Options include: Supabase (easiest, auto-generates PostgREST API), self-hosted PostgREST (for existing PostgreSQL instances), Hasura (more powerful but more complex), or a custom REST API built with Express, FastAPI, or any web framework. Supabase is the most popular choice because the REST API is zero-configuration.

Is the Supabase anon key safe to use in Bubble's API Connector without marking it Private?

Yes — with properly configured Row Level Security. The anon key is intentionally designed to be client-visible; security is enforced by RLS policies at the database level. However, marking the header as Private is still good practice (it hides the key from Bubble's workflow logs). If you have NOT enabled RLS on a table, the anon key gives anyone read access to all rows — always enable RLS first.

What happens if my Supabase free project pauses?

Free Supabase projects pause after approximately one week of database inactivity. API calls to a paused project take 10–20 seconds to respond as the project restarts. This creates a poor user experience for real apps. For production, upgrade to Supabase Pro ($25/month) to disable pausing. For staging or low-traffic apps, add a lightweight ping call on your Bubble index page load to proactively wake the project.

Can I use Supabase Auth to manage users in Bubble, or do I need Bubble's native auth?

Bubble has its own native user system that most founders use for managing app users. Supabase Auth is a separate system designed for apps built with code. For most Bubble apps, use Bubble's native auth and treat Supabase purely as a data layer — not as the auth provider. If you need Supabase's RLS policies to check `auth.uid()`, you'll need a custom JWT strategy where Bubble generates tokens Supabase can verify, which is an advanced setup.

Why does my repeating group show empty data even though the Supabase table has rows?

The most common causes are: (1) RLS is enabled with a policy that requires authentication, and the API call is using the anon key without a user JWT — so RLS blocks all rows; (2) the filter parameter in the API call doesn't match any rows; (3) the Initialize call was done on an empty table and Bubble didn't detect the correct response structure. Check Supabase Logs → API for 403 errors indicating RLS rejection, and verify the API Connector call's parameters in the Logs tab.

RapidDev

Talk to an Expert

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

Book a free consultation

Integrations are where projects stall

We wire Bubble integrations like this one every week — auth, webhooks, edge cases included. Fixed-price builds from $13K, delivered in 6–10 weeks.

Talk to an integration engineer

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.