Raw tables are an experimental feature. We’re actively seeking feedback on:
- API design and developer experience
- Additional features or optimizations needed
AvailabilityRaw tables were introduced in the following versions of our client SDKs:
- JavaScript (Node:
0.8.0
, React-Native:1.23.0
, Web:1.24.0
) - Dart: Version 1.15.0 of
package:powersync
. - Kotlin: Version 1.3.0
- Swift: Version 1.3.0
When to Use Raw Tables
Consider raw tables when you need:- Indexes - PowerSync’s default schema has basic support for indexes on columns, while raw tables give you complete control to create indexes on expressions, use
GENERATED
columns, etc - Improved performance for complex queries (e.g.,
SELECT SUM(value) FROM transactions
) - raw tables more efficiently get these values directly from the SQLite column, instead of extracting the value from the JSON object on every row - Reduced storage overhead - eliminate JSON object overhead for each row in
ps_data__<table>.data
column - To manually create tables - Sometimes you need full control over table creation, for example when implementing custom triggers
Advanced SQLite features like
FOREIGN KEY
and ON DELETE CASCADE
constraints may need special handling. If your use case requires these features, please reach out to us for guidance and potential workarounds.How Raw Tables Work
Current JSON-Based System
Currently the sync system involves two general steps:- Download sync bucket operations from the PowerSync Service
- Once the client has a complete checkpoint and no pending local changes in the upload queue, sync the local database with the bucket operations
ps_data__<table_name>
structure containing only an id
(TEXT) and data
(JSON) column.
PowerSync automatically creates views on that table that extract JSON fields to resemble standard tables reflecting your schema.
Raw Tables Approach
When opting in to raw tables, you are responsible for creating the tables before using them - PowerSync will no longer create them automatically. Because PowerSync takes no control over raw tables, you need to manually:- Define how PowerSync’s schemaless protocol maps to your raw tables — see Define sync mapping for raw tables
- Define triggers that capture local writes from raw tables — see Capture local writes with triggers
Define sync mapping for raw tables
To sync into the rawtodo_lists
table instead of ps_data__
, PowerSync needs the SQL statements extracting
columns from the untyped JSON protocol used during syncing.
This involves specifying two SQL statements:
- A
put
SQL statement for upserts, responsible for creating atodo_list
row or updating it based on itsid
and data columns. - A
delete
SQL statement responsible for deletions.
delete
statements can reference the id of the affected row, while put
statements can also reference individual column values.
Declaring these statements and parameters happens as part of the schema passed to PowerSync databases:
Raw tables are not included in the regular We will simplify this API after understanding the use-cases for raw tables better.
Schema()
object. Instead, add them afterwards using withRawTables
.
For each raw table, specify the put
and delete
statement. The values of parameters are described as a JSON
array either containing:- the string
Id
to reference the id of the affected row. - the object
{ Column: name }
to reference the value of the columnname
.
CREATE TABLE
statement before connect()
-ing the database.
Capture local writes with triggers
PowerSync uses an internal SQLite table to collect local writes. For PowerSync-managed views, a trigger for insertions, updates and deletions automatically forwards local mutations into this table. When using raw tables, defining those triggers is your responsibility. The PowerSync SQLite extension creates an insert-only virtual table namedpowersync_crud
with these columns:
Migrations
In PowerSync’s JSON-based view system the client-side schema is applied to the schemaless data, meaning no migrations are required. Raw tables however are excluded from this, so it is the developers responsibility to manage migrations for these tables.Adding raw tables as a new table
When you’re adding new tables to your sync rules, clients will start to sync data on those tables - even if the tables aren’t mentioned in the client’s schema yet. So at the time you’re introducing a new raw table to your app, it’s possible that PowerSync has already synced some data for that table, which would be stored inps_untyped
. When adding regular tables, PowerSync will automatically extract rows from ps_untyped
.
With raw tables, that step is your responsibility. To copy data, run these statements in a transaction after creating the table:
connect()
without them) - you only
need this for raw tables you already had locally.
Another workaround is to clear PowerSync data when changing raw tables and opt for a full resync.
Migrating to raw tables
To migrate from PowerSync-managed tables to raw tables, first:- Open the database with the new schema mentioning raw tables. PowerSync will copy data from tables previously managed by PowerSync into
ps_untyped
. - Create raw tables.
- Run the
INSERT FROM SELECT
statement to insertps_untyped
data into your raw tables.
Migrations on raw tables
When adding new columns to raw tables, there currently isn’t a way to re-sync that table to add those columns from the server - we are investigating possible workarounds and encourage users to try out if they need this. To ensure the column values are accurate, you’d have to delete all data after a migration and wait for the next complete sync.Deleting data and raw tables
APIs that clear an entire PowerSync database, like e.g.disconnectAndClear()
, don’t affect raw tables.
This should be kept in mind when you’re using those methods - data from raw tables needs to be deleted explicitly.