Skip to main content
The PowerSync integration is fully built and production-ready. The cloud sync component will become publicly available when the Despia V4 editor launches. To enable it already contact our support via the Despia live chat and share your PowerSync URL + Bundle ID.

Installation


Check for the native runtime

Returns true when the native PowerSync runtime is present, false in a standard browser. Use this to guard all database calls when your web app also runs outside Despia.
active() only confirms runtime presence. It does not mean SQLite is initialized or sync is connected.

Initialize the database

Call once at startup, before any queries, migrations, or sync calls. Registers the schema and target version with native as pending state.
db.init() validates the schema object before calling native. If validation fails it throws a PowerSyncError with code: "invalid_schema" and a details array listing every path that failed.

Read the active schema state

Returns the schema currently active in native. Use this to determine which migrations are still pending.

Run migrations

Apply pending SQL statements to the SQLite file. Native tracks the installed version and only executes statements for versions higher than the current one. Pass all pending statements for a version in a single call so native can commit or roll back the full upgrade as one transaction.
When your schema is at version N and the device needs migrations 1 through N, pass all pending statements in one db.migrate(N, statements) call rather than calling migrate() once per version.

Query multiple rows

Fetches all matching rows from local SQLite. Instant, no network.

Query a single row

Fetches the first matching row from local SQLite. Returns null if no row matches.

Write a single row

Runs one INSERT, UPDATE, or DELETE statement.

Write multiple rows atomically

Runs an array of write statements as one batch. All statements succeed or none do.
Returns { results: ExecuteResult[] }, one result per statement.

Run a transaction

Runs a group of statements with full rollback on any failure.
If any statement throws, the entire transaction is rolled back. The tx object exposes the same execute API as db.execute.

Subscribe to a live query

Fires the callback immediately with the current result set, then again whenever matching data changes, including rows updated by sync.
Call the returned function to stop the subscription. Always call it when the subscribing component or screen unmounts.

Connect to cloud sync

Starts sync for the signed-in user. Pass a short-lived JWT minted by your backend. Native reads the static PowerSync app ID and instance URL from native config.
Do not call this before db.init() and migrations. Sync will fail silently or throw if no active schema exists.

Trigger a manual sync

Schedules an immediate sync cycle. Sync completes asynchronously.
Treat db.powersync.sync() as a trigger, then read the result with db.powersync.status() or subscribe with db.powersync.events.status().

Read current sync state

Returns a snapshot of the sync engine state.

Subscribe to sync state changes

Fires the callback whenever sync state changes. Use this for sync indicators, offline banners, and last-synced timestamps. For row data, use db.watch() instead.

Upload local writes to your backend

Register a handler that native calls when it has pending CRUD writes to send to your backend. Use this when your upload runs in JavaScript.
If the handler resolves, the SDK tells native upload_complete and the native queue is finalized. If the handler throws, the SDK reports the error back to native and PowerSync retries later. Calling events.upload() again replaces the previous handler, so register one handler per session.

Create a search index

Creates or updates a native full-text index on a table. Resolves when the index is fully built, including asynchronous builds on large tables. Native validates that the source table and columns exist.
Native uses SQLite FTS5 for full-text and prefix search, and native trigram/edit-distance ranking for mode: "fuzzy" when supported by the app build.

Check search index build state

Returns the current native build state for a search index. Useful for showing progress while a large index builds.
Also available as db.search.indexStatus('postsSearch') for compatibility.

Subscribe to search index build events

Subscribe to the native async build lifecycle. Useful for showing progress in the UI while db.search.index() or db.search.rebuildIndex() waits.
db.search.events.index.progress, db.search.events.index.built, and db.search.events.index.failed are aliases for compatibility.

List search indexes

Returns all native search indexes registered for the database.

Query a search index

Queries a native search index with plain, prefix, or fuzzy matching.

Rebuild a search index

Rebuilds the index from the current table data. Resolves when the rebuild is complete. Use this after large migrations or data repairs.

Drop a search index

Removes a search index permanently.

PowerSync auth and tokens

db.powersync.connect({ token }) gives the native sync engine a token for the current signed-in user. It does not log the user into your app and does not create the token. Three separate pieces are involved: your app auth decides who the user is, PowerSync Client Auth verifies the token native sends to PowerSync, and PowerSync sync rules decide which rows that verified user can sync. The token identifies the user. If a row syncs into local SQLite, your app can query it. Private rows must be blocked by sync rules before they reach the device.

Token flow

Your backend signs the JWT with the key or secret configured in PowerSync Client Auth. PowerSync verifies the signature, kid, audience, expiry, and subject.

PowerSync dashboard setup

1

Open your PowerSync project

Go to powersync.com and open your project or instance.
2

Configure Client Auth

Go to Client Auth and configure the same JWT verification method your backend uses.
3

Configure sync rules

Set up sync rules or streams that use the authenticated user identity to control which rows each user can sync.
For custom JWT auth, PowerSync expects a signed JWT:
For production, asymmetric JWT signing with JWKS is preferred so PowerSync can verify tokens with a public key while your backend keeps the private signing key. HS256 works for development if it exactly matches your PowerSync Client Auth configuration.

Mint a token on your backend

Keep signing keys on the server. The frontend receives only a short-lived token for the authenticated user.

Fetch the token on the client

Supabase and Firebase

If your app already uses Supabase Auth or Firebase Auth, you may not need a custom token endpoint. PowerSync can verify those provider JWTs directly when Client Auth, audience, and JWKS settings match your provider.

Migrations reference

Schema describes the expected shape. Migration SQL changes the actual SQLite file. You always need both. Keep schema and migrations together in one module:
When the schema changes: update CURRENT_SCHEMA, increase SCHEMA_VERSION, add a migration with the new version, run all pending statements with db.migrate(SCHEMA_VERSION, pendingStatements), and only then run queries or sync that depend on the new shape.

Add a column

Add a table

Rename or reshape a table

Use copy-and-swap. SQLite does not support ALTER TABLE ... RENAME COLUMN on older versions.

Errors

db.init(), db.migrate(), and other methods throw a PowerSyncError when validation fails.

Error codes

Validation reasons

Fallback on failed migration

If a migration fails, do not start sync with the new schema. Fall back to the previously active schema if your app can run against it.

Standard PowerSync flow

This is the correct call order on every app start. The active() guard is required when your web app also runs outside Despia.

TypeScript types


Exports


Resources

NPM Package

@despia/powersync

GitHub

despia-native/despia-powersync

PowerSync

Backend setup, schema config, and sync rules

Check for the native runtime

Returns true when the native PowerSync runtime is present, false in a standard browser. Use this to guard all database calls when your web app also runs outside Despia.
active() only confirms runtime presence. It does not mean SQLite is initialized or sync is connected.

Initialize the database

Call once at startup, before any queries, migrations, or sync calls. Registers the schema and target version with native as pending state.
db.init() validates the schema object before calling native. If validation fails it throws a PowerSyncError with code: "invalid_schema" and a details array listing every path that failed.

Read the active schema state

Returns the schema currently active in native. Use this to determine which migrations are still pending.

Run migrations

Apply pending SQL statements to the SQLite file. Native tracks the installed version and only executes statements for versions higher than the current one. Pass all pending statements for a version in a single call so native can commit or roll back the full upgrade as one transaction.
When your schema is at version N and the device needs migrations 1 through N, pass all pending statements in one db.migrate(N, statements) call rather than calling migrate() once per version.

Query multiple rows

Fetches all matching rows from local SQLite. Instant, no network.

Query a single row

Fetches the first matching row from local SQLite. Returns null if no row matches.

Write a single row

Runs one INSERT, UPDATE, or DELETE statement.

Write multiple rows atomically

Runs an array of write statements as one batch. All statements succeed or none do.
Returns { results: ExecuteResult[] }, one result per statement.

Run a transaction

Runs a group of statements with full rollback on any failure.
If any statement throws, the entire transaction is rolled back. The tx object exposes the same execute API as db.execute.

Subscribe to a live query

Fires the callback immediately with the current result set, then again whenever matching data changes, including rows updated by sync.
Call the returned function to stop the subscription. Always call it when the subscribing component or screen unmounts.

Connect to cloud sync

Starts sync for the signed-in user. Pass a short-lived JWT minted by your backend. Native reads the static PowerSync app ID and instance URL from native config.
Do not call this before db.init() and migrations. Sync will fail silently or throw if no active schema exists.

Trigger a manual sync

Schedules an immediate sync cycle. Sync completes asynchronously.
Treat db.powersync.sync() as a trigger, then read the result with db.powersync.status() or subscribe with db.powersync.events.status().

Read current sync state

Returns a snapshot of the sync engine state.

Subscribe to sync state changes

Fires the callback whenever sync state changes. Use this for sync indicators, offline banners, and last-synced timestamps. For row data, use db.watch() instead.

Upload local writes to your backend

Register a handler that native calls when it has pending CRUD writes to send to your backend. Use this when your upload runs in JavaScript.
If the handler resolves, the SDK tells native upload_complete and the native queue is finalized. If the handler throws, the SDK reports the error back to native and PowerSync retries later. Calling events.upload() again replaces the previous handler, so register one handler per session.

Create a search index

Creates or updates a native full-text index on a table. Resolves when the index is fully built, including asynchronous builds on large tables. Native validates that the source table and columns exist.
Native uses SQLite FTS5 for full-text and prefix search, and native trigram/edit-distance ranking for mode: "fuzzy" when supported by the app build.

Check search index build state

Returns the current native build state for a search index. Useful for showing progress while a large index builds.
Also available as db.search.indexStatus('postsSearch') for compatibility.

Subscribe to search index build events

Subscribe to the native async build lifecycle. Useful for showing progress in the UI while db.search.index() or db.search.rebuildIndex() waits.
db.search.events.index.progress, db.search.events.index.built, and db.search.events.index.failed are aliases for compatibility.

List search indexes

Returns all native search indexes registered for the database.

Query a search index

Queries a native search index with plain, prefix, or fuzzy matching.

Rebuild a search index

Rebuilds the index from the current table data. Resolves when the rebuild is complete. Use this after large migrations or data repairs.

Drop a search index

Removes a search index permanently.

PowerSync auth and tokens

db.powersync.connect({ token }) gives the native sync engine a token for the current signed-in user. It does not log the user into your app and does not create the token. Three separate pieces are involved: your app auth decides who the user is, PowerSync Client Auth verifies the token native sends to PowerSync, and PowerSync sync rules decide which rows that verified user can sync. The token identifies the user. If a row syncs into local SQLite, your app can query it. Private rows must be blocked by sync rules before they reach the device.

Token flow

Your backend signs the JWT with the key or secret configured in PowerSync Client Auth. PowerSync verifies the signature, kid, audience, expiry, and subject.

PowerSync dashboard setup

1

Open your PowerSync project

Go to powersync.com and open your project or instance.
2

Configure Client Auth

Go to Client Auth and configure the same JWT verification method your backend uses.
3

Configure sync rules

Set up sync rules or streams that use the authenticated user identity to control which rows each user can sync.
For custom JWT auth, PowerSync expects a signed JWT:
For production, asymmetric JWT signing with JWKS is preferred so PowerSync can verify tokens with a public key while your backend keeps the private signing key. HS256 works for development if it exactly matches your PowerSync Client Auth configuration.

Mint a token on your backend

Keep signing keys on the server. The frontend receives only a short-lived token for the authenticated user.

Fetch the token on the client

Supabase and Firebase

If your app already uses Supabase Auth or Firebase Auth, you may not need a custom token endpoint. PowerSync can verify those provider JWTs directly when Client Auth, audience, and JWKS settings match your provider.

Migrations reference

Schema describes the expected shape. Migration SQL changes the actual SQLite file. You always need both. Keep schema and migrations together in one module:
When the schema changes: update CURRENT_SCHEMA, increase SCHEMA_VERSION, add a migration with the new version, run all pending statements with db.migrate(SCHEMA_VERSION, pendingStatements), and only then run queries or sync that depend on the new shape.

Add a column

Add a table

Rename or reshape a table

Use copy-and-swap. SQLite does not support ALTER TABLE ... RENAME COLUMN on older versions.

Errors

db.init(), db.migrate(), and other methods throw a PowerSyncError when validation fails.

Error codes

Validation reasons

Fallback on failed migration

If a migration fails, do not start sync with the new schema. Fall back to the previously active schema if your app can run against it.

Standard PowerSync flow

This is the correct call order on every app start. The active() guard is required when your web app also runs outside Despia.

TypeScript types

Exports

Resources