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 .claude/CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ Start with action-oriented language: "Use [component] to..." rather than "The [c
- Use relative links for internal pages: `/sync/streams/overview`
- Update `docs.json` when adding, moving, or removing pages
- Add redirects in `docs.json` for any moved content
- Check for broken links: `npx mintlify broken-links`
- Check for broken links: `npx mintlify broken-links`. Mintlify requires Node 20.17–24; if the default `node` is newer, prefix with `PATH="/opt/homebrew/opt/node@24/bin:$PATH"`
- Never use absolute URLs for internal links

## Sync Streams and Sync Rules
Expand Down
4 changes: 2 additions & 2 deletions .claude/commands/lint-docs.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
---
allowed-tools: Bash(npx mintlify *), Bash(vale *), Bash(git diff *)
allowed-tools: Bash(npx mintlify *), Bash(PATH=* npx mintlify *), Bash(vale *), Bash(git diff *)
description: Run documentation linting checks — broken links, Vale prose style, and build validation. Use before publishing or merging documentation changes.
---

Run all documentation linting checks and report any issues.

1. Run `npx mintlify broken-links` to find broken internal links
1. Run `npx mintlify broken-links` to find broken internal links. Mintlify requires Node 20.17–24; if the default `node` is 25 or newer, run it with an LTS Node instead: `PATH="/opt/homebrew/opt/node@24/bin:$PATH" npx mintlify broken-links` (install with `brew install node@24` if missing).
2. Run `git diff main --name-only -- '*.mdx'` to get the list of changed MDX files
3. For each changed file, run `vale <file>` to check prose style
4. Report all broken links and Vale violations, grouped by file
Expand Down
9 changes: 6 additions & 3 deletions sync/advanced/partitioned-tables.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -31,16 +31,19 @@ To use the same queries and same output table name for each partition, use `%` f
</Tab>
</Tabs>

The wildcard character can only be used as the last character in the table name.
The wildcard character can only be used as the last character in the table name. To match tables across multiple schemas instead, see [Wildcard Schemas](/sync/advanced/schemas-and-connections#wildcard-schemas-postgres).

When using wildcard table names, the original table suffix is available in the special `_table_suffix` column. This works the same way in both Sync Streams and Sync Rules:
When using wildcard table names, you can filter on the original table suffix. In Sync Streams, use the `table_suffix()` function, prefixed with the table name or alias from the `FROM` clause (requires PowerSync Service v1.24.0 or later). In legacy Sync Rules, the suffix is available as the special `_table_suffix` column instead:

<Tabs>
<Tab title="Sync Streams">
```yaml
config:
edition: 3

streams:
active_todos:
query: SELECT * FROM "todos_%" AS todos WHERE _table_suffix != 'archived'
query: SELECT * FROM "todos_%" AS todos WHERE todos.table_suffix() != 'archived'
```
</Tab>
<Tab title="Sync Rules (Legacy)">
Expand Down
27 changes: 26 additions & 1 deletion sync/advanced/schemas-and-connections.mdx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
title: "Schemas and Connections"
description: "Configure Postgres schema usage in Sync Streams/Rules queries, connect to high-availability replicas, and plan for multiple database connections."
description: "Configure Postgres schema usage in Sync Streams/Rules queries, including wildcard schemas for schema-per-tenant setups, and connect to high-availability replicas."
---

## Schemas (Postgres)
Expand All @@ -12,6 +12,31 @@ When no schema is specified, the Postgres `public` schema is used for every quer
SELECT * FROM "other"."assets"
```

## Wildcard Schemas (Postgres)

<Note>
Wildcard schemas require [Sync Streams](/sync/streams/overview) and PowerSync Service v1.24.0 or later. They are currently only supported for Postgres connections.
</Note>

Use `%` as a wildcard in the schema name to match tables with the same name across multiple schemas. `"%"` matches every schema, and a prefix such as `"tenant_%"` matches every schema whose name starts with `tenant_`. The wildcard can only be the last character of the schema name. Postgres system schemas (`pg_*` and `information_schema`) are never matched.

Combine a wildcard schema with the [`schema()` function](/sync/supported-sql#functions), which returns the schema each row was replicated from, to filter rows by schema. This supports schema-per-tenant databases (a single database with one identical schema per tenant): one stream covers every tenant schema, and each client syncs only its own tenant's data, resolved from a JWT claim.

```yaml
config:
edition: 3

streams:
work_orders:
query: SELECT * FROM "%".work_orders WHERE work_orders.schema() = auth.parameter('tenant_schema')
```

In this example, rows are grouped into a bucket per schema, and each client syncs only the bucket matching the `tenant_schema` claim in its JWT. Rows from all matched schemas sync into a single client-side table, named after the table in the query (`work_orders` here).

<Note>
Each matched table must be part of the [PowerSync publication](/configuration/source-db/setup#3-create-powersync-publication). Tables that are not in the publication are skipped.
</Note>
Comment thread
benitav marked this conversation as resolved.

## High Availability / Replicated Databases (Postgres)

When the source Postgres database is replicated, for example with Amazon RDS Multi-AZ deployments, specify a single connection with multiple host endpoints. Each host endpoint will be tried in sequence, with the first available primary connection being used.
Expand Down
15 changes: 15 additions & 0 deletions sync/streams/examples.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,21 @@ sub.unsubscribe();

For more complex organization structures where users can belong to multiple organizations, see [Expanding JSON Arrays](/sync/streams/parameters#expanding-json-arrays).

### Schema-per-Tenant Data (Postgres)

For multi-tenant Postgres databases with one identical schema per tenant, use a wildcard schema with the `schema()` function. A single stream covers every tenant schema, and each client syncs only the schema named by the `tenant_schema` claim in its JWT:

```yaml
config:
edition: 3

streams:
work_orders:
query: SELECT * FROM "%".work_orders WHERE work_orders.schema() = auth.parameter('tenant_schema')
```

This requires PowerSync Service v1.24.0 or later. See [Wildcard Schemas](/sync/advanced/schemas-and-connections#wildcard-schemas-postgres) for requirements and details.

### Role-Based Access

When different users should see different data based on their role, use JWT claims to apply visibility rules. This keeps authorization logic on the server side where it's secure.
Expand Down
9 changes: 9 additions & 0 deletions sync/supported-sql.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,15 @@ Most functions are from [SQLite built-in functions](https://www.sqlite.org/lang_
- **[datetime(time-value, [modifier])](https://www.sqlite.org/lang_datefunc.html)** — Returns a time-value as a date and time string, in the format YYYY-MM-DD HH:MM:SS. If the specifier is "subsec", milliseconds are also included. If the modifier is "unixepoch", the argument is interpreted as a Unix timestamp. Both modifiers can be included: `datetime(timestamp, 'unixepoch', 'subsec')`. The time-value argument is required — this function cannot be used to get the current time.
- **[uuid_blob(id)](https://sqlite.org/src/file/ext/misc/uuid.c)** — Convert a UUID string to bytes.
</Accordion>
<Accordion title="Table metadata">
These functions return metadata about the table a row comes from, rather than operating on a value. Prefix each call with the name or alias of a table in the query's `FROM` clause, the same way you would qualify a column reference: with `FROM "todos_%" AS todos`, write `todos.table_suffix()`. The functions take no arguments, and can appear in `WHERE` clauses, in selected columns, and inside subqueries.

- **schema()** — Returns the schema the row was replicated from. Combined with a [wildcard schema](/sync/advanced/schemas-and-connections#wildcard-schemas-postgres), this allows filtering rows by their source schema, e.g. `SELECT * FROM "%".work_orders WHERE work_orders.schema() = auth.parameter('tenant_schema')`.
- **table_name()** — Returns the name of the table the row was replicated from. This is the source table's name, not the alias or the output table name.
- **table_suffix()** — Returns the part of the table name matched by the trailing `%` of a [wildcard table name](/sync/advanced/partitioned-tables). For example, with `FROM "todos_%" AS todos`, `todos.table_suffix()` returns `2024` for rows from the `todos_2024` table. On tables without a wildcard name, the result is always empty and the compiler reports a warning.

<Info>Supported in Sync Streams only, with PowerSync Service v1.24.0 or later.</Info>
</Accordion>
<Accordion title="GIS (PostGIS)">
- **[ST_AsGeoJSON(geometry)](/client-sdks/advanced/gis-data-postgis)** — Convert [PostGIS](/client-sdks/advanced/gis-data-postgis) (in Postgres) geometry from WKB to GeoJSON. Combine with JSON operators to extract specific fields.
- **[ST_AsText(geometry)](/client-sdks/advanced/gis-data-postgis)** — Convert [PostGIS](/client-sdks/advanced/gis-data-postgis) (in Postgres) geometry from WKB to Well-Known Text (WKT).
Expand Down