Video walkthrough of the integration guide.

Used in conjunction with Supabase, PowerSync enables developers to build local-first & offline-first apps that are robust in poor network conditions and that have highly responsive frontends while relying on Supabase for their backend. This guide provides instructions for how to configure PowerSync for use with your Supabase project.

Before you proceed, this guide assumes that you have already signed up for free accounts with both Supabase and PowerSync Cloud (our cloud-hosted offering). If you haven’t signed up for a PowerSync (Cloud) account yet, click here (and if you haven’t signed up for Supabase yet, click here).

For mobile/desktop apps, this guide assumes that you already have Flutter / React Native / Kotlin Multiplatform / Xcode set up.

For web apps, this guide assumes that you have pnpm installed.

This guide takes 10-15 minutes to complete.

Architecture

Upon successful integration of Supabase + PowerSync, your system architecture will look like this: (click to enlarge image)

The local SQLite database embedded in the PowerSync SDK is automatically kept in sync with the Supabase Postgres database (based on configured sync rules as you will see later in this guide). Client-side data modifications are persisted in the local SQLite database as well as stored in an upload queue that gets processed via the Supabase client library when network connectivity is available. Therefore reads and writes can happen in the app regardless of whether the user is online or offline, by using the local SQLite database.

For more details on PowerSync’s general architecture, see here.

Integration Guide/Tutorial Overview

We will follow these steps to get an offline-first ‘To-Do List’ demo app up and running:

1

Configure Supabase:

  • Create the demo database schema
  • Create the Postgres publication
2

Configure PowerSync:

  • Create connection to Supabase
  • Configure Sync Rules
3

Test the configuration

Test the configuration using our provided PowerSync-Supabase ‘To-Do List’ demo app with your framework of choice.

Configure Supabase

Create a new Supabase project (or use an existing project if you prefer) and follow the below steps.

Create the Demo Database Schema

To set up the Postgres database for our To-Do List demo app, we will create two new tables: lists and todos. The demo app will have access to these tables even while offline.

Run the below SQL statements in your Supabase SQL Editor: (if you get a warning about a “potentially destructive operation”, that’s a false positive that you can safely ignore.)

create table
  public.lists (
    id uuid not null default gen_random_uuid (),
    created_at timestamp with time zone not null default now(),
    name text not null,
    owner_id uuid not null,
    constraint lists_pkey primary key (id),
    constraint lists_owner_id_fkey foreign key (owner_id) references auth.users (id) on delete cascade
  ) tablespace pg_default;

create table
  public.todos (
    id uuid not null default gen_random_uuid (),
    created_at timestamp with time zone not null default now(),
    completed_at timestamp with time zone null,
    description text not null,
    completed boolean not null default false,
    created_by uuid null,
    completed_by uuid null,
    list_id uuid not null,
    constraint todos_pkey primary key (id),
    constraint todos_created_by_fkey foreign key (created_by) references auth.users (id) on delete set null,
    constraint todos_completed_by_fkey foreign key (completed_by) references auth.users (id) on delete set null,
    constraint todos_list_id_fkey foreign key (list_id) references lists (id) on delete cascade
  ) tablespace pg_default;

Create the Postgres Publication

PowerSync uses the Postgres Write Ahead Log (WAL) to replicate data changes in order to keep PowerSync SDK clients up to date.

Run the below SQL statement in your Supabase SQL Editor:

-- Create publication for powersync

create publication powersync for table public.lists, public.todos;

Note: this guide uses the default postgres user in your Supabase account for replicating changes to PowerSync, since elevating custom roles to replication has been disabled in Supabase. If you want to use a custom role for this purpose, contact the Supabase support team.

Configuring PowerSync

Create a PowerSync Cloud Instance

  1. In the Overview workspace of the PowerSync Dashboard you will be prompted to create your first instance:

If you previously created an instance, you can create a secondary instance by navigating to the Manage instances workspace and clicking on Create new instance.

  1. Give your instance a name, such as “Testing”.
  2. [Optional] You can change the default cloud region from US to EU, JP (Japan), AU (Australia) or BR (Brazil) if desired.
    • Note: Additional cloud regions will be considered on request, especially for customers on our Enterprise plan. Please contact us if you need a different region.
  3. [Optional] You can opt in to using the Beta version of the Service, which may contain early access or experimental features. Always use the Stable version in production.
  4. Click Next.

Connect PowerSync to Your Supabase

  1. From your Supabase Dashboard, select Connect in the top navigation bar:
  • In the Direct connection section, copy the complete connection string (including the [YOUR-PASSWORD] placeholder)
  • Paste the connection string into the PowerSync instance URI field. PowerSync will automatically parse this URI to populate the connection details.

  • For the Password field, enter your database’s postgres user password (this is your database/project password, not your Supabase account password). Need help? Check Supabase’s guide on managing database passwords.

  • Note: PowerSync includes Supabase’s CA certificate by default, so you can use verify-full SSL mode without additional configuration.

Your connection settings should look similar to this:

  1. Verify your setup by clicking Test Connection and resolve any errors.

  2. Click Next.

  3. PowerSync will detect the Supabase connection and prompt you to enable Supabase auth. To enable it, copy your JWT Secret from the Supabase Dashboard’s API settings and paste it into the form:

  4. Click Enable Supabase auth to finalize your connections settings.

You can update your instance settings by navigating to the Manage instances workspace, opening your instance options and selecting Edit instance.

PowerSync will now create an isolated cloud environment for your instance. This process typically takes a few minutes.

Configure Sync Rules

Sync Rules allow developers to control which data gets synced to which user devices using a SQL-like syntax in a YAML file. For the demo app, we’re going to specify that each user can only see their own to-do lists and list items.

  1. The final step is to replace the Sync Rules file’s contents with the below:
bucket_definitions:
  user_lists:
    # Separate bucket per To-Do list
    parameters: select id as list_id from lists where owner_id = request.user_id()
    data:
      - select * from lists where id = bucket.list_id
      - select * from todos where list_id = bucket.list_id
  1. Click “Validate sync rules” and ensure there are no errors. This validates your sync rules against your Postgres database.
  2. Click “Save and deploy” to deploy your Sync Rules.
  • Your Sync Rules can be updated by navigating to the Manage instances workspace and selecting the sync-rules.yaml file.
  • For additional information on PowerSync’s Sync Rules, refer to the Sync Rules documentation.
  • If you’re wondering how Sync Rules relate to Supabase Postgres RLS, see this subsection.

Test Everything (Using Our Demo App)

In this step you’ll test your setup using a ‘To-Do List’ demo app provided by PowerSync.

Clone the demo app

Clone the demo app based on your framework:

git clone https://github.com/powersync-ja/powersync.dart.git
cd powersync.dart/demos/supabase-todolist/

Configure the demo app to use your PowerSync instance

Locate the relevant config file for your framework:

cp lib/app_config_template.dart lib/app_config.dart

# Edit `lib/app_config.dart` and insert the necessary credentials as detailed below.
  1. In the relevant config file, replace the values for supabaseUrl and supabaseAnonKey (you can find these under “Project Settings” -> “API” in your Supabase dashboard — under the “URL” section, and anon key under “Project API keys”)
  2. For the value of powersyncUrl, click the copy icon on your instance to copy its URL:

Run the app

# Ensure you have [melos](https://melos.invertase.dev/~melos-latest/getting-started) installed.

melos bootstrap
flutter run

For ease of use of the demo app, you can disable email confirmation in your Supabase Auth settings. In your Supabase project, go to “Authentication” -> “Providers” -> “Email” and then disable “Confirm email”. If you keep email confirmation enabled, the Supabase user confirmation email will reference the default Supabase Site URL ofhttp://localhost:3000 — you can ignore this.

Once signed in to the demo app, you should see a blank list of to-do lists, so go ahead and create a new list. Try placing your device into airplane mode to test out the offline capabilities. Once the device is back online, you should see the data automatically appear in your Supabase dashboard (e.g. in the Table Editor).

For more information, explore the PowerSync docs or join us on our community Discord where our team is always available to answer questions.

Bonus: Optional Extras

If you plan on sharing this demo app with other people, you may want to set up demo data triggers so that new user signups don’t see an empty screen.

It’s useful to have some data when a user signs up to the demo app. The below trigger automatically creates some sample data when a user registers (you can run it in the Supabase SQL Editor). See Supabase: Managing User Data for more details.

create function public.handle_new_user_sample_data()
returns trigger as $$
declare
  new_list_id uuid;
begin
  insert into public.lists (name, owner_id)
    values ('Shopping list', new.id)
    returning id into new_list_id;

  insert into public.todos(description, list_id, created_by)
    values ('Bread', new_list_id, new.id);

  insert into public.todos(description, list_id, created_by)
    values ('Apples', new_list_id, new.id);

  return new;
end;
$$ language plpgsql security definer;

create trigger new_user_sample_data after insert on auth.users for each row execute procedure public.handle_new_user_sample_data();