Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion client-sdks/reference/capacitor.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ export class Connector {
// Implement uploadData to send local changes to your backend service.
// You can omit this method if you only want to sync data from the database to the client

// See example implementation here: https://docs.powersync.com/client-sdks/reference/javascript-web#3-integrate-with-your-backend
// See example implementation here: https://docs.powersync.com/client-sdks/usage-examples#send-changes-in-local-data-to-your-backend-service
Comment thread
benitav marked this conversation as resolved.
}
}
```
Expand Down
2 changes: 1 addition & 1 deletion client-sdks/reference/flutter.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ class MyBackendConnector extends PowerSyncBackendConnector {

// Implement uploadData to send local changes to your backend service
// You can omit this method if you only want to sync data from the server to the client
Comment thread
benitav marked this conversation as resolved.
// See example implementation here: https://docs.powersync.com/client-sdks/reference/flutter#3-integrate-with-your-backend
// See example implementation here: https://docs.powersync.com/client-sdks/usage-examples#send-changes-in-local-data-to-your-backend-service
@override
Future<void> uploadData(PowerSyncDatabase database) async {
// This function is called whenever there is data to upload, whether the
Expand Down
2 changes: 1 addition & 1 deletion client-sdks/reference/javascript-web.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ export class Connector {
// Implement uploadData to send local changes to your backend service.
// You can omit this method if you only want to sync data from the database to the client

// See example implementation here: https://docs.powersync.com/client-sdks/reference/javascript-web#3-integrate-with-your-backend
// See example implementation here: https://docs.powersync.com/client-sdks/usage-examples#send-changes-in-local-data-to-your-backend-service
}
}
```
Expand Down
2 changes: 1 addition & 1 deletion client-sdks/reference/node.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ export class Connector implements PowerSyncBackendConnector {
// Implement uploadData to send local changes to your backend service.
// You can omit this method if you only want to sync data from the database to the client

// See example implementation here: https://docs.powersync.com/client-sdks/reference/javascript-web#3-integrate-with-your-backend
// See example implementation here: https://docs.powersync.com/client-sdks/usage-examples#send-changes-in-local-data-to-your-backend-service
Comment thread
benitav marked this conversation as resolved.
}
}
```
Expand Down
2 changes: 1 addition & 1 deletion client-sdks/reference/react-native-and-expo.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ export class Connector implements PowerSyncBackendConnector {
/**
* Implement uploadData to send local changes to your backend service.
* You can omit this method if you only want to sync data from the database to the client
Comment thread
benitav marked this conversation as resolved.
* See example implementation here:https://docs.powersync.com/client-sdks/reference/react-native-and-expo#3-integrate-with-your-backend
* See example implementation here: https://docs.powersync.com/client-sdks/usage-examples#send-changes-in-local-data-to-your-backend-service
*/
async uploadData(database: AbstractPowerSyncDatabase) {

Expand Down
50 changes: 48 additions & 2 deletions client-sdks/usage-examples.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -551,11 +551,57 @@ import JavaScriptCallbackWatch from '/snippets/basic-watch-query-javascript-call
</Tab>

<Tab title="Capacitor">
Example not yet available.
Override [uploadData](https://github.com/powersync-ja/powersync-js/blob/ed5bb49b5a1dc579050304fab847feb8d09b45c7/packages/common/src/client/connection/PowerSyncBackendConnector.ts#L24) to send local updates to your backend service.

```js
// Implement the uploadData method in your backend connector
async function uploadData(database) {
const batch = await database.getCrudBatch();
if (batch === null) return;

for (const op of batch.crud) {
switch (op.op) {
case 'put':
// Send the data to your backend service
// replace `_myApi` with your own API client or service
await _myApi.put(op.table, op.opData);
break;
default:
// TODO: implement the other operations (patch, delete)
break;
}
}

await batch.complete();
}
```
</Tab>

<Tab title="Node.js">
Example not yet available.
Override [uploadData](https://github.com/powersync-ja/powersync-js/blob/ed5bb49b5a1dc579050304fab847feb8d09b45c7/packages/common/src/client/connection/PowerSyncBackendConnector.ts#L24) to send local updates to your backend service.

```js
// Implement the uploadData method in your backend connector
async function uploadData(database) {
const batch = await database.getCrudBatch();
if (batch === null) return;

for (const op of batch.crud) {
switch (op.op) {
case 'put':
// Send the data to your backend service
// replace `_myApi` with your own API client or service
await _myApi.put(op.table, op.opData);
break;
default:
// TODO: implement the other operations (patch, delete)
break;
}
}

await batch.complete();
}
```
</Tab>

<Tab title="Kotlin">
Expand Down
4 changes: 2 additions & 2 deletions maintenance-ops/deploying-schema-changes.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,9 @@ See the appropriate subsections below for details on the various scenarios.
</Accordion>

<Accordion title="Renaming a Table on the Client">
Pass in a "`schema_version`" or similar parameter from the client, and use this in Sync Rules to use either the old or new table name in the data queries.
Define a versioned stream alongside the old one, or pass in a `schema_version` or similar parameter from the client and use it to choose either the old or new table name in the queries.

See this section for details:
See this section for details on both approaches:

[Multiple Client Versions](/sync/advanced/multiple-client-versions)
</Accordion>
Expand Down
2 changes: 1 addition & 1 deletion maintenance-ops/implementing-schema-changes.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ The [client-side schema](/intro/setup-guide#define-your-client-side-schema) is j

The developer is responsible for keeping client-side schema changes backwards-compatible with older versions of client apps. PowerSync has some functionality to assist with this:

1. [Different stream queries](/sync/advanced/multiple-client-versions) can be applied based on [connection parameters](/sync/streams/parameters#connection-parameters) such as client version. (In Sync Rules, this uses [client parameters](/sync/rules/client-parameters).)
1. [Versioned streams](/sync/advanced/multiple-client-versions) can serve different data to different client versions, either by defining a separate stream per version or by filtering on [connection parameters](/sync/streams/parameters#connection-parameters) such as client version. (In Sync Rules, this uses [client parameters](/sync/rules/client-parameters).)

2. Stream queries can apply simple data transformations to keep data in a format compatible with older clients, for example by aliasing or casting columns. (In Sync Rules, this is done via [data query expressions](/sync/rules/data-queries).)

Expand Down
48 changes: 39 additions & 9 deletions sync/advanced/multiple-client-versions.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,53 @@ title: "Multiple Client Versions"
description: "Handle multiple client app versions that require different output schemas from Sync Streams."
---

When schema changes are additive, old clients would just ignore the new tables and columns, and no special handling is required. However, in some cases, the schema changes may be more drastic and may need separate Sync Streams (or Sync Rules) based on the client version.
When schema changes are additive, old clients ignore the new tables and columns, and no special handling is required. More drastic changes, such as renaming tables or changing a table's structure, can break older app versions that are still in use. In these cases, define separate versions of the affected [Sync Streams](/sync/streams/overview) so that each client version receives the tables and columns it expects.

To distinguish between client versions, clients can pass version information to the PowerSync Service. In [Sync Streams](/sync/streams/overview), these are called connection parameters (accessed via `connection.parameter()`). In legacy [Sync Rules](/sync/rules/overview), these are called [client parameters](/sync/rules/client-parameters).
## Versioning by Stream Name

Example to use different table names based on the client's `schema_version`:
With Sync Streams, the most convenient approach is usually to define a new stream alongside the old one. New app versions subscribe to the new stream, while older app versions continue subscribing to the old one.

For example, suppose a new app version changes the structure of the `assets` table in its [client-side schema](/intro/setup-guide#define-your-client-side-schema), defining it as `assets_v2`, while older app versions still define `assets`. Define a new stream alongside the existing one, using an alias to map the source `assets` table to the new client-side name:

```yaml
streams:
# Old stream, kept for backward compatibility with older app versions.
# Remove it once those versions are no longer in use.
user_assets:
query: SELECT * FROM assets WHERE user_id = auth.user_id()

# New app versions subscribe to this stream.
# The alias maps the table to the new client-side name.
user_assets_v2:
query: SELECT * FROM assets AS assets_v2 WHERE user_id = auth.user_id()
```

```js
// New app versions subscribe to the new stream
const subscription = await db.syncStream('user_assets_v2').subscribe();
```

Once the older app versions are no longer in use, remove the old stream from your configuration and deploy the change.

## Versioning with Connection Parameters
Comment thread
benitav marked this conversation as resolved.

Alternatively, clients can pass their version to the PowerSync Service as a [connection parameter](/sync/streams/parameters#connection-parameters), and stream queries filter on it so each client only receives data for its version. This approach is useful when your streams are auto-subscribed: auto-subscribed streams sync to every client on connect, so clients cannot select a stream version by name. In legacy [Sync Rules](/sync/rules/overview), connection parameters are called [client parameters](/sync/rules/client-parameters).

The example below implements the same `assets` use case, with both stream versions auto-subscribed and filtered by a `schema_version` connection parameter:

<Tabs>
<Tab title="Sync Streams">
```yaml
# Client passes connection params: {"schema_version": <version>}
streams:
assets_v1:
query: SELECT * FROM assets AS assets_v1
user_assets:
auto_subscribe: true
query: SELECT * FROM assets
WHERE user_id = auth.user_id()
AND connection.parameter('schema_version') = '1'

assets_v2:
user_assets_v2:
auto_subscribe: true
query: SELECT * FROM assets AS assets_v2
WHERE user_id = auth.user_id()
AND connection.parameter('schema_version') = '2'
Expand All @@ -28,13 +58,13 @@ Example to use different table names based on the client's `schema_version`:
<Tab title="Sync Rules (Legacy)">
```yaml
# Client passes in: "params": {"schema_version": <version>}
assets_v1:
user_assets:
parameters: SELECT request.user_id() AS user_id
WHERE request.parameters() ->> 'schema_version' = '1'
data:
- SELECT * FROM assets AS assets_v1 WHERE user_id = bucket.user_id
- SELECT * FROM assets WHERE user_id = bucket.user_id

assets_v2:
user_assets_v2:
parameters: SELECT request.user_id() AS user_id
WHERE request.parameters() ->> 'schema_version' = '2'
data:
Expand Down
4 changes: 2 additions & 2 deletions sync/streams/overview.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ sidebarTitle: "Quickstart"

import StreamDefinitionReference from '/snippets/stream-definition-reference.mdx';

Instead of syncing entire tables, you tell PowerSync exactly which data each user/client can sync. You write simple SQL-like queries to define streams of data, and your client app subscribes to the streams it needs. PowerSync handles the rest, keeping data in sync in real-time and making it available offline.
With Sync Streams, you write simple SQL-like queries to define streams of data, and your client app subscribes to the streams it needs. This enables _partial sync_: each client syncs only the relevant subset of data, instead of the entire database. PowerSync handles the rest, keeping subscribed data synced in real-time to a client-side SQLite database, where it stays available even when the device is offline.

For example, you might create a stream that syncs only the current user's to-do items, another for shared projects they have access to, and another for reference data that everyone needs. Your app subscribes to these streams on demand, and only that data syncs to the client-side SQLite database. Offline-first apps that need all relevant data available upfront can use `auto_subscribe: true` so streams sync automatically when clients connect.
For example, you might define a stream that syncs only the current user's to-do items, another for shared projects they have access to, and another for reference data that everyone needs. Your app subscribes to these streams on demand, and only that data syncs to the device. Offline-first apps that need all relevant data available upfront can use `auto_subscribe: true` so streams sync automatically when clients connect.

<Note>
**Are you still using Sync Rules?** Sync Streams support everything Sync Rules do, plus more expressive queries (including JOIN support), on-demand syncing, and a simpler developer experience (e.g. React hooks that manage subscriptions automatically).
Expand Down