Skip to main content
This page covers query syntax for Sync Streams: filtering, selecting columns, and transforming data. For parameter usage, see Using Parameters. For real-world patterns, see Examples, Patterns & Demos.

Basic Queries

The simplest stream query syncs all rows from a table:
Add a WHERE clause to filter:

Filtering by User

Most apps need to sync different data to different users. Use auth.user_id() to filter by the authenticated user:
This syncs only the lists owned by the current user. The user ID comes from the sub claim in their JWT token. See Auth Parameters.

On-Demand Data with Subscription Parameters

For data that should only sync when the user navigates to a specific screen, use subscription parameters. The client passes these when subscribing to a stream:
Authorization: This example filters only by subscription.parameter('list_id'). Any client can pass any list_id, so a user could access another user’s todos. For production, add an authorization check so the user can only see lists they own or have access to — for example, add AND list_id IN (SELECT id FROM lists WHERE owner_id = auth.user_id() OR id IN (SELECT list_id FROM list_shares WHERE shared_with = auth.user_id())). See Combining Parameters with Subqueries below.
See Using Parameters for the full reference on parameters.

Selecting Columns

Select specific columns instead of * to reduce data transfer:

Renaming Columns

Use AS to rename columns in the synced data:

Type Transformations

PowerSync syncs data to SQLite on the client. You may need to transform types for compatibility:
See Type Mapping for details on how each database type is handled.

Using Subqueries

Subqueries let you filter based on related tables. Use IN (SELECT ...) to sync data where a foreign key matches rows in another table:

Nested Subqueries

Subqueries can be nested to traverse multiple levels of relationships. This is useful for normalized database schemas:

Combining Parameters with Subqueries

A common pattern is using subscription parameters to select what data to sync, while using subqueries for authorization:

Using Joins

For complex queries that traverse multiple tables, join syntax is often easier to read than nested subqueries. You can use JOIN or INNER JOIN (they’re equivalent). For the exact supported JOIN syntax and restrictions, see Supported SQL — JOIN syntax. Consider this query:
The same query using joins:
Both queries sync the same data. Choose whichever style is clearer for your use case.

Multiple Joins

You can chain multiple joins to traverse complex relationships. This example joins four tables to sync checkpoints for assignments the user has access to.

Self-Joins

You can join the same table multiple times; aliases are required to distinguish each occurrence (e.g. gm1 and gm2 for the two group_memberships joins). This is useful for finding related records through a shared relationship — for example, finding all users who share a group with the current user:

Join Limitations

When writing stream queries with JOINs, keep in mind: use only JOIN or INNER JOIN; select columns from a single table (e.g. comments.*); and use simple equality conditions (table1.column = table2.column). For the full list of supported JOIN syntax and invalid examples, see Supported SQL — JOIN syntax.

Multiple Queries per Stream

You can group multiple queries into a single stream using queries instead of query. This is useful when several tables share the same access pattern:
You subscribe once to the stream; PowerSync merges the data from all queries efficiently. This is than defining separate streams, each requiring its own subscription.

When to Use Multiple Queries

Use queries when:
  • Multiple tables have the same filtering logic (e.g., all filtered by user_id)
  • You want to optimize sync by using one stream so the client subscribes once and PowerSync merges the data from all queries, and to reduce bucket count ()
  • Related data should sync together

Compatibility Requirements

For multiple queries in one stream to be valid, they must use compatible parameter inputs. In practice, this means they should filter on the same parameters in the same way:

Combining with CTEs

Multiple queries work well with Common Table Expressions (CTEs) to share the filtering logic and keep all results in one stream, requiring clients to manage one subscription instead of many:

Complete Example

A full configuration combining multiple techniques:
See Examples & Patterns for real-world examples like multi-tenant apps and role-based access, and Supported SQL for all available operators and functions.