> ## Documentation Index
> Fetch the complete documentation index at: https://powersync-wildcard-schemas-docs.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Multiple Client Versions

> Handle multiple client app versions that require different output schemas from Sync Streams.

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.

## Versioning by Stream Name

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 theme={null}
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 theme={null}
// 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

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 theme={null}
    # Client passes connection params: {"schema_version": <version>}
    streams:
      user_assets:
        auto_subscribe: true
        query: SELECT * FROM assets
               WHERE user_id = auth.user_id()
               AND connection.parameter('schema_version') = '1'

      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'
    ```
  </Tab>

  <Tab title="Sync Rules (Legacy)">
    ```yaml theme={null}
    # Client passes in: "params": {"schema_version": <version>}
      user_assets:
        parameters: SELECT request.user_id() AS user_id
          WHERE request.parameters() ->> 'schema_version' = '1'
        data:
          - SELECT * FROM assets WHERE user_id = bucket.user_id

      user_assets_v2:
        parameters: SELECT request.user_id() AS user_id
          WHERE request.parameters() ->> 'schema_version' = '2'
        data:
          - SELECT * FROM assets AS assets_v2 WHERE user_id = bucket.user_id
    ```
  </Tab>
</Tabs>

<Warning>
  Handle queries based on parameters set by the client with care. The client can send any value for these parameters, so it's not a good place to do authorization. If the parameter must be authenticated, use parameters from the JWT instead.
</Warning>
