Vue SDK
Package: @loomup/vue (SDK repository path: packages/vue).
Thin Vue 3 bindings on top of @loomup/client: plugin, auth session, REST query composables, and realtime live queries.
Install
# from a project that can reach the monorepo paths
npm install ../path/to/packages/client ../path/to/packages/vue
# or after publish:
# npm install @loomup/client @loomup/vue
Peer dependencies: vue ≥ 3.3, @loomup/client ≥ 0.1.0.
Quick start
// main.ts
import { createApp } from "vue";
import { createClient } from "@loomup/client";
import { LoomupPlugin } from "@loomup/vue";
import App from "./App.vue";
// Keep the client stable (module scope).
const client = createClient({ url: "http://127.0.0.1:3000" });
const app = createApp(App);
app.use(LoomupPlugin, {
client,
persist: { enabled: true },
});
app.mount("#app");
<script setup lang="ts">
import { useAuth, useLiveQuery, useMutation } from "@loomup/vue";
const { user, loading: authLoading, signIn, signUp, signOut } = useAuth();
const { data, loading, ready } = useLiveQuery("todos", {
sort: "-id",
limit: 50,
// "refetch" (default) is safest with where/rules;
// "merge" applies INSERT/UPDATE/DELETE/RESYNC to local state.
strategy: "merge",
enabled: () => !!user.value,
});
const { mutate: addTodo } = useMutation((c, title: string) =>
c.from("todos").insert({ title, completed: 0 }),
);
</script>
<template>
<p v-if="authLoading">Loading…</p>
<button
v-else-if="!user"
type="button"
@click="signIn({ email: 'a@b.com', password: 'secret12' })"
>
Sign in
</button>
<div v-else>
<p>
{{ user.email }}
<button type="button" @click="signOut()">Sign out</button>
</p>
<button type="button" @click="addTodo('Ship Vue SDK')">Add</button>
<p v-if="loading">Loading todos…</p>
<ul>
<li v-for="row in data ?? []" :key="String(row.id)">
{{ row.title }}
</li>
</ul>
<small v-if="ready">Live</small>
</div>
</template>
You can also pass options={{ url }} instead of client and the plugin will create a client once at install.
For tests or nested trees without app.use, call provideLoomup({ client }) from a parent setup().
API
LoomupPlugin
app.use(LoomupPlugin, {
client, // existing LoomupClient (preferred)
// options, // CreateClientOptions when client is omitted
persist: {
enabled: true, // store tokens in localStorage
storageKey: "loomup", // key prefix
},
});
| Option | Description |
|---|---|
client | Existing LoomupClient (preferred). |
options | CreateClientOptions when client is omitted. |
persist.enabled | Store access/refresh tokens in localStorage. |
persist.storageKey | Key prefix (default loomup). |
provideLoomup(options)
Same options as the plugin; uses Vue provide for the current component tree.
useLoomup()
Returns the client from inject. Throws outside the plugin / provideLoomup.
useAuth()
Reactive fields are Vue Refs (auto-unwrapped in templates).
| Field | Description |
|---|---|
user | Current user or null. |
session | { accessToken, refreshToken }. |
loading / error | Async state. |
signIn / signUp / signOut / refresh / me | Session methods. |
On mount, if the client already has an access token (or one is restored from storage), calls auth.me().
useSelect(table, opts?) / useRow(table, id, opts?)
REST list / get with loading, error, refetch. Options: where, sort, limit, offset, enabled.
enabled and useRow's id accept plain values, refs, or getters (MaybeRefOrGetter).
useMutation(fn)
const { mutate, loading, error, reset } = useMutation((client, ...args) =>
client.from("todos").insert(...),
);
useSubscribe(table, handler, opts?)
Subscribes on setup; unsubscribes on unmount. Uses subscribeReady by default (waitForAck: true). Handler identity can change without resubscribing.
useLiveQuery(table, opts?)
Initial select + realtime subscription.
strategy | Behavior |
|---|---|
refetch (default) | Re-run select on every change (correct with filters/rules). |
merge | Apply INSERT/UPDATE/DELETE/RESYNC into the local array. |
Filtered lists cannot perfectly re-evaluate server where/rules client-side; prefer refetch when filters matter.
Types
Use generated maps from the core SDK:
loomup gen typescript --config loomup.toml --output ./loomup-types.ts
import { createClient } from "@loomup/client";
import type { TableMap, TableInsertMap, TableUpdateMap } from "./loomup-types";
const client = createClient<TableMap, TableInsertMap, TableUpdateMap>({
url: "http://127.0.0.1:3000",
});
@loomup/vue re-exports createClient, LoomupError, and common types for convenience.
Example
cd examples/todo-vue && npm install && npm run dev
# against a running loomup serve on :3000
Publish workflow (maintainers)
- Publish or link
@loomup/clientfirst. - Bump
packages/vue/package.jsonversion. npm test/npm run buildinpackages/vue.npm publish --access public.
Non-goals (v1)
- Nuxt module / cookie SSR helpers (browser / SPA first).
- Vue 2 / Options API–only wrappers.
- Offline sync.