diff --git a/packages/pglite/README.md b/packages/pglite/README.md index 0164a375..9ff7c9dd 100644 --- a/packages/pglite/README.md +++ b/packages/pglite/README.md @@ -359,6 +359,127 @@ PGlite supports the pl/pgsql procedural language extension, this is included and In future we plan to support additional extensions, see the [roadmap](#roadmap). +## Live Queries + +The "live" extension enables you to subscribe to a query and receve updated results when the underlying tables change. + +To use the extension it needs adding to the PGlite instance when creating it: + +```ts +import { PGlite } from "@electric-sql/pglite"; +import { live } from "@electric-sql/pglite/live"; + +const pg = new PGlite({ + extensions: { + live, + }, +}); +``` + +There are three methods on the `live` namespace: +- `live.query()` for basic live queries. Less machinery in PG so quicker for small results sets and narrow rows. +- `live.incrementalQuery()` for incremental queries. It materialises the full result set on each update in js. Perfect for feeding into React +- `live.changes()` a lower level API that emits the changes (insert/update/delete) that can then be mapped to mutation in a UI or other datastore. + +### live.query() + +This is very similar to a standard query, but takes an additional callback that receives the results whenever they change: + +```js +const ret = pg.live.query("SELECT * FROM test ORDER BY rand;", null, (res) => { + // res is the same as a standard query result object +}); +``` + +The returned value from the call is an object with this interface: + +```ts +interface LiveQueryReturn { + initialResults: Results; + unsubscribe: () => Promise; + refresh: () => Promise; +} +``` + +- `initialResults` is the initial results set (also sent to the callback +- `unsubscribe` allow you to unsubscribe from the live query +- `refresh` allows you to force a refresh of the query + +Internally it watches for the tables that the query depends on, and reruns the query whenever they are changed. + +### live.incrementalQuery() + +Similar to above, but maintains a temporary table inside of Postgres of the previous state. When the tables it depends on change the query is re-run and diffed with the last state. Only the changes from the last version of the query are copied from WASM into JS. + +It requires an additional `key` argument, the name of a column (often a PK) to key the diff on. + +```js +const ret = pg.live.incrementalQuery( + "SELECT * FROM test ORDER BY rand;", [], "id", + (res) => { + // res is the same as a standard query result object + } +); +``` + +The returned value is of the same type as the `query` method above. + +### live.changes() + +A lower level API which is the backend for the `incrementalQuery`, it emits the change that have happened. It requires a `key` to key the diff on: + +``` +const ret = pg.live.changes( + "SELECT * FROM test ORDER BY rand;", [], "id", + (res) => { + // res is a change result object + } +); +``` + +the returned value from the call is defined by this interface: + +```ts +interface LiveChangesReturn { + fields: { name: string; dataTypeID: number }[]; + initialChanges: Array>; + unsubscribe: () => Promise; + refresh: () => Promise; +} +``` + +The results passed to the callback are array of `Change` objects: + +```ts +type ChangeInsert = { + __changed_columns__: string[]; + __op__: "INSERT"; + __after__: number; +} & T; + +type ChangeDelete = { + __changed_columns__: string[]; + __op__: "DELETE"; + __after__: undefined; +} & T; + +type ChangeUpdate = { + __changed_columns__: string[]; + __op__: "UPDATE"; + __after__: number; +} & T; + +type Change = ChangeInsert | ChangeDelete | ChangeUpdate; +``` + +Each `Change` includes the new values along with: + +- `__changed_columns__` the columns names that were changes +- `__op__` the operation that is required to update the state (`INSERT`, `UPDATE`, `DELETE`) +- `__after__` the `key` of the row that this row should be after, it will be included in `__changed_columns__` if it has been changed. + +This API can be used to implement very efficient in-place DOM updates. + ## ORM support. - Drizzle ORM supports PGlite, see [their docs here](https://orm.drizzle.team/docs/get-started-postgresql#pglite).