@ductape/vue
v0.0.3
Published
Vue composables for Ductape
Maintainers
Readme
@ductape/vue
Vue 3 composables for Ductape. Access databases, storage, APIs, agents, brokers, and more with reactive composables.
Install
npm install @ductape/vue @ductape/clientSetup
Register the plugin in main.ts:
import { createApp } from 'vue';
import { createDuctape } from '@ductape/vue';
import App from './App.vue';
const app = createApp(App);
app.use(createDuctape({
publishableKey: import.meta.env.VITE_PUBLISHABLE_KEY,
product: 'my-product',
env: 'prd',
}));
app.mount('#app');Add autoConnect for real-time features:
app.use(createDuctape({
publishableKey: import.meta.env.VITE_PUBLISHABLE_KEY,
product: 'my-product',
env: 'prd',
autoConnect: true,
}));Every request through the publishable key requires a session token — your backend issues this after the user logs in.
Composables
Access the client directly
<script setup>
import { useDuctape } from '@ductape/vue';
const { client, isConnected, connect, disconnect } = useDuctape();
</script>Database
<script setup>
import { useDatabaseQuery, useDatabaseInsert, useDatabaseSubscription } from '@ductape/vue';
import { computed } from 'vue';
const props = defineProps({ sessionToken: String });
const { data, isLoading, error, refetch } = useDatabaseQuery(
['orders', 'pending'],
{ database: 'orders-db', table: 'orders', where: { status: 'pending' }, limit: 20, session: props.sessionToken }
);
const { mutate: createOrder } = useDatabaseInsert({ onSuccess: () => refetch() });
useDatabaseSubscription(
{ database: 'orders-db', table: 'orders', session: props.sessionToken },
(changes) => console.log(changes),
{ enabled: computed(() => !!props.sessionToken) }
);
</script>
<template>
<div v-if="isLoading">Loading...</div>
<ul v-else>
<li v-for="row in data?.rows" :key="row.id">{{ row.id }}</li>
</ul>
<button @click="createOrder({ database: 'orders-db', table: 'orders', data: { amount: 4900 }, session: sessionToken })">
New order
</button>
</template>Third-party API actions
<script setup>
import { useActionRun } from '@ductape/vue';
const props = defineProps({ sessionToken: String });
const { mutate: run, isLoading } = useActionRun({
onSuccess: (result) => console.log(result),
});
</script>
<template>
<button
:disabled="isLoading"
@click="run({ app: 'paystack', action: 'verify-payment', input: { 'query:reference': 'abc123' }, session: sessionToken })"
>
Verify payment
</button>
</template>Features
<script setup>
import { useFeatureExecute, useFeatureStatus, useFeatureSubscription } from '@ductape/vue';
import { computed } from 'vue';
const props = defineProps({ orderId: String, sessionToken: String });
const { mutate: run, data: execution } = useFeatureExecute();
const executionId = computed(() => execution.value?.executionId ?? '');
const { data: status } = useFeatureStatus(
{ feature: 'process-order', executionId: executionId.value },
{ enabled: computed(() => !!executionId.value) }
);
useFeatureSubscription(
{ feature: 'process-order', executionId: executionId.value },
(events) => console.log(events),
{ enabled: computed(() => !!executionId.value) }
);
</script>
<template>
<button @click="run({ feature: 'process-order', input: { orderId }, session: sessionToken })">Run</button>
<p v-if="status">Status: {{ status.status }}</p>
</template>Agents
<script setup>
import { useAgentRun, useAgentStream } from '@ductape/vue';
const props = defineProps({ sessionToken: String });
const { mutate: ask, data } = useAgentRun();
const { start, tokens, isStreaming } = useAgentStream({ agent: 'support-bot' });
</script>
<template>
<button @click="start('How do I reset my password?', { session: sessionToken })">Ask</button>
<p v-if="isStreaming">{{ tokens }}</p>
</template>Storage
<script setup>
import { useUpload, useListFiles } from '@ductape/vue';
const props = defineProps({ sessionToken: String });
const { upload, progress, isUploading } = useUpload({ storage: 'receipts', session: props.sessionToken });
const { data: files } = useListFiles({ storage: 'receipts', session: props.sessionToken });
function handleFile(event) {
const file = event.target.files?.[0];
if (file) upload({ fileName: file.name, data: file, mimeType: file.type });
}
</script>
<template>
<input type="file" @change="handleFile" />
<p v-if="isUploading">Uploading {{ progress }}%</p>
<ul>
<li v-for="f in files" :key="f.name">{{ f.name }}</li>
</ul>
</template>Cache
<script setup>
import { useCacheGet, useCacheSet } from '@ductape/vue';
const props = defineProps({ sessionToken: String });
const { data: theme } = useCacheGet({ cache: 'app-cache', key: 'theme', session: props.sessionToken });
const { mutate: save } = useCacheSet();
</script>
<template>
<button @click="save({ cache: 'app-cache', key: 'theme', value: 'dark', ttl: 86400, session: sessionToken })">
Save theme
</button>
</template>Brokers
<script setup>
import { useBrokerPublish, useBrokerSubscription } from '@ductape/vue';
import { computed } from 'vue';
const props = defineProps({ sessionToken: String });
const { mutate: publish } = useBrokerPublish();
useBrokerSubscription(
{ broker: 'kafka-cluster', topic: 'order-events', session: props.sessionToken },
(messages) => console.log(messages),
{ enabled: computed(() => !!props.sessionToken) }
);
</script>
<template>
<button @click="publish({ broker: 'kafka-cluster', topic: 'order-events', message: { type: 'ping' }, session: sessionToken })">
Publish
</button>
</template>Vectors
<script setup>
import { useVectorQuery } from '@ductape/vue';
const props = defineProps({ embedding: Array, sessionToken: String });
const { data: results } = useVectorQuery(
['search', props.embedding],
{ vector: 'product-embeddings', query: props.embedding, topK: 10, session: props.sessionToken }
);
</script>Warehouse
<script setup>
import { useWarehouseQuery } from '@ductape/vue';
const props = defineProps({ sessionToken: String });
const { data } = useWarehouseQuery(
['revenue-report'],
{
query: 'SELECT SUM(amount) as total FROM orders WHERE status = :status',
params: { status: 'completed' },
sources: [{ database: 'orders-db', tables: ['orders'] }],
session: props.sessionToken,
}
);
</script>Resilience
<script setup>
import { useQuotaCheck, useHealthStatus } from '@ductape/vue';
const props = defineProps({ sessionToken: String });
const { data: quota } = useQuotaCheck({ tag: 'api-rate-limit', session: props.sessionToken });
const { data: health } = useHealthStatus({ tag: 'payment-service', session: props.sessionToken });
</script>Notifications
<script setup>
import { useEmailNotification } from '@ductape/vue';
const props = defineProps({ sessionToken: String });
const { mutate: sendEmail } = useEmailNotification();
</script>
<template>
<button @click="sendEmail({ notification: 'welcome-email', input: { email: '[email protected]' }, session: sessionToken })">
Send welcome
</button>
</template>Analytics
<script setup>
import { useAnalytics } from '@ductape/vue';
import { onMounted } from 'vue';
const props = defineProps({ sessionToken: String });
const analytics = useAnalytics();
onMounted(() => {
if (props.sessionToken) analytics.identify(props.sessionToken);
analytics.track({ event: 'page_view', properties: { page: '/dashboard' } });
});
</script>