diff --git a/.changeset/big-mugs-admire.md b/.changeset/big-mugs-admire.md
new file mode 100644
index 000000000..d5fc87108
--- /dev/null
+++ b/.changeset/big-mugs-admire.md
@@ -0,0 +1,5 @@
+---
+"@powersync/vue": minor
+---
+
+Introduced `useQuery` and `useStatus` composables which include compilable query support. Deprecated `usePowerSyncQuery`, `usePowerSyncWatchedQuery`, and `usePowerSyncStatus`.
diff --git a/.changeset/seven-sheep-smell.md b/.changeset/seven-sheep-smell.md
new file mode 100644
index 000000000..170ec5042
--- /dev/null
+++ b/.changeset/seven-sheep-smell.md
@@ -0,0 +1,5 @@
+---
+"vue-supabase-todolist": patch
+---
+
+Using new Vue `useQuery` and `useStatus` composables instead of deprecated ones.
diff --git a/demos/vue-supabase-todolist/src/components/LoadingMessage.vue b/demos/vue-supabase-todolist/src/components/LoadingMessage.vue
index 8cfa023f5..c841ccd03 100644
--- a/demos/vue-supabase-todolist/src/components/LoadingMessage.vue
+++ b/demos/vue-supabase-todolist/src/components/LoadingMessage.vue
@@ -14,27 +14,27 @@
diff --git a/demos/vue-supabase-todolist/src/components/widgets/TodoListsWidget.vue b/demos/vue-supabase-todolist/src/components/widgets/TodoListsWidget.vue
index 4aea95035..14456484e 100644
--- a/demos/vue-supabase-todolist/src/components/widgets/TodoListsWidget.vue
+++ b/demos/vue-supabase-todolist/src/components/widgets/TodoListsWidget.vue
@@ -1,6 +1,6 @@
-
+ {{ error.message }}
@@ -18,10 +18,10 @@
```
-### Accessing PowerSync
+## Accessing PowerSync
The provided PowerSync client is available with the `usePowerSync` composable.
@@ -65,75 +65,108 @@ powersync.value.getAll('SELECT * from lists').then((l) => list.value = l);
```
-### Queries
+## Query
-The `usePowerSyncQuery` composable provides a static view of a given query. You can use refs as parameters instead to automatically refresh the query when they change. The composable exposes reactive variables for the results, the loading state and error state, as well as a refresh callback that can be invoked to rerun the query manually.
+The `useQuery` composable provides a dynamic view of a given query. The data will automatically update when a dependent table is updated.
+
+You can use refs as parameters to refresh the query when they change. The composable exposes reactive variables for the results as well as the loading, fetching, and and error states. Note that `isLoading` indicates that the initial result is being retrieved and `isFetching` indicates the query is fetching data, which could be for the initial load or any time when the query is re-evaluating due to a change in a dependent table.
```Vue
// TodoListDisplayQuery.vue
-
Loading...
-
{{ error }}
+
Loading...
+
Updating results...
+
+
{{ error }}
{{ l.name }}
-
+
```
-### Watched Queries
+### Static query
-The `usePowerSyncWatchedQuery` composable provides a dynamic view of a given query. The data will automatically update when a dependent table is updated.
-
-You can use refs as parameters to refresh the query when they change. The composable exposes reactive variables for the results as well as the loading, fetching, and and error states. Note that `loading` initicates that the initial result is being retrieved and `fetching` indicates the query is fetching data, which could be for the initial load or any time when the query is re-evaluating due to a change in a dependent table.
+The `useQuery` composable can be configured to only execute initially and not every time changes to dependent tables are detected. The query can be manually re-executed by using the returned `refresh` function.
```Vue
-// TodoListDisplayWatchedQuery.vue
+// TodoListDisplayStaticQuery.vue
-
-
Loading...
-
Updating results...
+
+
{{ l.name }} + {{ l.id }}
+
+
+
-
{{ error }}
-
-
{{ l.name }}
-
-
+```
+
+### TypeScript Support
+
+A type can be specified for each row returned by `useQuery`. Remember to declare `lang="ts"` when defining a `script setup` block.
+
+```Vue
+// TodoListDisplayStaticQueryTypeScript.vue
+
+
+
+
+
{{ l.name }}
+
```
-### Connection Status
+You are also able to use a compilable query (e.g. [Kysely queries](https://github.com/powersync-ja/powersync-js/tree/main/packages/kysely-driver)) as a query argument in place of a SQL statement string.
+
+```Vue
+// TodolistDisplayQueryKysely.vue
+
+```
+
+## Connection Status
-The `usePowerSyncStatus` composable provides general connectivity information such as the connection status, whether the initial full sync has completed, when the last sync completed, and whether any data is being uploaded or downloaded.
+The `useStatus` composable provides general connectivity information such as the connection status, whether the initial full sync has completed, when the last sync completed, and whether any data is being uploaded or downloaded.
```Vue
// ConnectionStatus.vue
@@ -150,7 +183,7 @@ const { status } = usePowerSyncStatus();
### Top-level setup block
-The `usePowersync`, `usePowerSyncQuery`, `usePowerSyncWatchedQuery`, and `usePowerSyncStatus` composables are meant to be invoked in the top-level setup block. Vue expects certain Composition API functions, like `inject` which this package depends on, to be resolved in the setup context and not inside nested or asynchronous functions. For use cases where you need to do this, you should access the PowerSync `AbstractPowerSyncDatabase` instance directly - like exporting it as singleton after configuring Vue with it in `main.js`.
+The `usePowersync`, `useQuery`, and `useStatus` composables are meant to be invoked in the top-level setup block. Vue expects certain Composition API functions, like `inject` which this package depends on, to be resolved in the setup context and not inside nested or asynchronous functions. For use cases where you need to do this, you should access the PowerSync `AbstractPowerSyncDatabase` instance directly - like exporting it as singleton after configuring Vue with it in `main.js`.
Incorrect Usage Example:
Using PowerSync composables in a nested function of a component.
diff --git a/packages/vue/package.json b/packages/vue/package.json
index 129cd9c3e..0fd462a37 100644
--- a/packages/vue/package.json
+++ b/packages/vue/package.json
@@ -14,7 +14,8 @@
"scripts": {
"build": "tsc -b",
"clean": "rm -rf lib tsconfig.tsbuildinfo",
- "watch": "tsc -b -w"
+ "watch": "tsc -b -w",
+ "test": "vitest"
},
"repository": {
"type": "git",
@@ -33,7 +34,10 @@
"vue": "*"
},
"devDependencies": {
+ "flush-promises": "^1.0.2",
+ "jsdom": "^24.0.0",
"typescript": "^5.1.3",
+ "vitest": "^1.5.1",
"vue": "3.4.21"
}
}
diff --git a/packages/vue/src/composables/usePowerSyncQuery.ts b/packages/vue/src/composables/usePowerSyncQuery.ts
index ec39e656a..016b94dfe 100644
--- a/packages/vue/src/composables/usePowerSyncQuery.ts
+++ b/packages/vue/src/composables/usePowerSyncQuery.ts
@@ -17,6 +17,8 @@ export type QueryOptions = {
export type Result = { data: Ref; loading: Ref; error: Ref; refresh: () => Promise };
/**
+ * @deprecated use {@link useQuery} instead.
+ *
* A composable to access a single static query.
* SQL Statement and query Parameters are watched by default.
* For a result that updates as the source data changes, use {@link usePowerSyncWatchedQuery} instead.
@@ -28,7 +30,7 @@ export const usePowerSyncQuery = (
): Result => {
const data = ref([]);
const loading = ref(false);
- const error = ref(undefined);
+ const error = ref(undefined);
const powerSync = usePowerSync();
@@ -47,7 +49,7 @@ export const usePowerSyncQuery = (
data.value = [];
const wrappedError = new Error('PowerSync failed to fetch data: ' + e.message);
- wrappedError.cause = e; // Include the original error as the cause
+ wrappedError.cause = e;
error.value = wrappedError;
} finally {
loading.value = false;
@@ -57,6 +59,7 @@ export const usePowerSyncQuery = (
if (queryOptions.watchParameters) {
watch([powerSync, ref(sqlStatement), ref(parameters)], fetchData);
}
+
if (queryOptions.immediate) {
fetchData();
}
diff --git a/packages/vue/src/composables/usePowerSyncStatus.ts b/packages/vue/src/composables/usePowerSyncStatus.ts
index daee81343..6afec3e2a 100644
--- a/packages/vue/src/composables/usePowerSyncStatus.ts
+++ b/packages/vue/src/composables/usePowerSyncStatus.ts
@@ -1,29 +1,32 @@
import { SyncStatus } from '@powersync/common';
-import { onUnmounted, ref } from 'vue';
+import { ref, watchEffect } from 'vue';
import { usePowerSync } from './powerSync';
+/**
+ * @deprecated Use {@link useStatus} instead.
+ */
export const usePowerSyncStatus = () => {
+ const powerSync = usePowerSync();
const status = ref(new SyncStatus({}));
- let cleanup = () => {};
- const powerSync = usePowerSync();
- if (powerSync) {
- status.value = powerSync.value.currentStatus || status.value;
+ if (!powerSync) {
+ return status;
+ }
+
+ status.value = powerSync.value.currentStatus || status.value;
- const disposeListener = powerSync.value.registerListener({
+ watchEffect((onCleanup) => {
+ const listener = powerSync.value.registerListener({
statusChanged: (newStatus: SyncStatus) => {
status.value = newStatus;
}
});
- cleanup = () => {
- disposeListener();
- };
- }
-
- onUnmounted(() => {
- cleanup();
+ // Cleanup previous listener when the effect triggers again, or when the component is unmounted
+ onCleanup(() => {
+ listener();
+ });
});
- return { status };
+ return status;
};
diff --git a/packages/vue/src/composables/usePowerSyncWatchedQuery.ts b/packages/vue/src/composables/usePowerSyncWatchedQuery.ts
index c3c7e4628..142f45365 100644
--- a/packages/vue/src/composables/usePowerSyncWatchedQuery.ts
+++ b/packages/vue/src/composables/usePowerSyncWatchedQuery.ts
@@ -16,6 +16,8 @@ export type WatchedQueryResult = {
};
/**
+ * @deprecated use {@link useQuery} instead.
+ *
* A composable to access the results of a watched query.
*/
export const usePowerSyncWatchedQuery = (
@@ -24,7 +26,7 @@ export const usePowerSyncWatchedQuery = (
options: Omit = {}
): WatchedQueryResult => {
const data = ref([]);
- const error = ref(undefined);
+ const error = ref(undefined);
const loading = ref(true);
const fetching = ref(true);
diff --git a/packages/vue/src/composables/useQuery.ts b/packages/vue/src/composables/useQuery.ts
new file mode 100644
index 000000000..707b4ffc2
--- /dev/null
+++ b/packages/vue/src/composables/useQuery.ts
@@ -0,0 +1,155 @@
+import { type SQLWatchOptions, parseQuery, type CompilableQuery, ParsedQuery } from '@powersync/common';
+import { type MaybeRef, type Ref, ref, toValue, watchEffect } from 'vue';
+import { usePowerSync } from './powerSync';
+
+interface AdditionalOptions extends Omit {
+ runQueryOnce?: boolean;
+}
+
+export type WatchedQueryResult = {
+ data: Ref;
+ /**
+ * Indicates the initial loading state (hard loading). Loading becomes false once the first set of results from the watched query is available or an error occurs.
+ */
+ isLoading: Ref;
+ /**
+ * Indicates whether the query is currently fetching data, is true during the initial load and any time when the query is re-evaluating (useful for large queries).
+ */
+ isFetching: Ref;
+ error: Ref;
+ /**
+ * Function used to run the query again.
+ */
+ refresh?: () => Promise;
+};
+
+/**
+ * A composable to access the results of a watched query.
+ *
+ * @example
+ * ```vue
+ *
+ *
+ *
+ *