@ductape/react
v0.0.3
Published
React hooks and components for Ductape
Maintainers
Readme
@ductape/react
React hooks for Ductape. Drop-in hooks for databases, storage, APIs, agents, brokers, and more.
Install
npm install @ductape/react @ductape/clientSetup
Wrap your app with DuctapeProvider:
import { DuctapeProvider } from '@ductape/react';
function App() {
return (
<DuctapeProvider
config={{
publishableKey: import.meta.env.VITE_PUBLISHABLE_KEY,
product: 'my-product',
env: 'prd',
}}
>
<MyApp />
</DuctapeProvider>
);
}Add autoConnect for real-time features:
<DuctapeProvider
config={{ publishableKey: import.meta.env.VITE_PUBLISHABLE_KEY, product: 'my-product', env: 'prd' }}
autoConnect
>
<MyApp />
</DuctapeProvider>Every request through the publishable key requires a session token — your backend issues this after the user logs in.
Hooks
Access the client directly
import { useDuctape } from '@ductape/react';
function MyComponent() {
const { client, isConnected, connect, disconnect } = useDuctape();
}Database
import { useDatabaseQuery, useDatabaseInsert, useDatabaseUpdate, useDatabaseDelete, useDatabaseSubscription } from '@ductape/react';
function Orders({ sessionToken }: { sessionToken: string }) {
const { data, isLoading, error, refetch } = useDatabaseQuery(
['orders', 'pending'],
{ database: 'orders-db', table: 'orders', where: { status: 'pending' }, limit: 20, session: sessionToken }
);
const { mutate: createOrder } = useDatabaseInsert({ onSuccess: () => refetch() });
const { mutate: updateOrder } = useDatabaseUpdate();
const { mutate: deleteOrder } = useDatabaseDelete();
// Real-time row changes (requires autoConnect)
useDatabaseSubscription(
{ database: 'orders-db', table: 'orders', session: sessionToken },
(changes) => console.log(changes),
{ enabled: !!sessionToken }
);
return (
<button onClick={() => createOrder({ database: 'orders-db', table: 'orders', data: { amount: 4900 }, session: sessionToken })}>
New order
</button>
);
}Third-party API actions
import { useActionRun } from '@ductape/react';
function ActionButton({ sessionToken }: { sessionToken: string }) {
const { mutate: run, isLoading } = useActionRun({
onSuccess: (result) => console.log(result),
});
return (
<button
disabled={isLoading}
onClick={() => run({ app: 'paystack', action: 'verify-payment', input: { 'query:reference': 'abc123' }, session: sessionToken })}
>
Verify payment
</button>
);
}Features
import { useFeatureExecute, useFeatureStatus, useFeatureSubscription } from '@ductape/react';
function ProcessOrder({ orderId, sessionToken }: { orderId: string; sessionToken: string }) {
const { mutate: run, data: execution } = useFeatureExecute({
onSuccess: (result) => console.log('started:', result.executionId),
});
const { data: status } = useFeatureStatus(
{ feature: 'process-order', executionId: execution?.executionId ?? '' },
{ enabled: !!execution }
);
useFeatureSubscription(
{ feature: 'process-order', executionId: execution?.executionId ?? '' },
(events) => console.log(events),
{ enabled: !!execution }
);
return (
<button onClick={() => run({ feature: 'process-order', input: { orderId }, session: sessionToken })}>
Run
</button>
);
}Agents
import { useAgentRun, useAgentStream } from '@ductape/react';
function SupportBot({ sessionToken }: { sessionToken: string }) {
const { mutate: ask, data } = useAgentRun();
const { start, tokens, isStreaming } = useAgentStream({ agent: 'support-bot' });
return (
<>
<button onClick={() => start('How do I reset my password?', { session: sessionToken })}>Ask</button>
{isStreaming && <p>{tokens}</p>}
</>
);
}Storage
import { useUpload, useListFiles, useSignedUrl, useStorageDelete } from '@ductape/react';
function FileManager({ sessionToken }: { sessionToken: string }) {
const { upload, progress, isUploading } = useUpload({ storage: 'receipts', session: sessionToken });
const { data: files } = useListFiles({ storage: 'receipts', session: sessionToken });
const { mutate: getUrl } = useSignedUrl();
const { mutate: remove } = useStorageDelete();
return (
<input
type="file"
onChange={(e) => {
const file = e.target.files?.[0];
if (file) upload({ fileName: file.name, data: file, mimeType: file.type });
}}
/>
);
}Cache
import { useCacheGet, useCacheSet, useCacheDelete } from '@ductape/react';
function Settings({ sessionToken }: { sessionToken: string }) {
const { data } = useCacheGet({ cache: 'app-cache', key: 'theme', session: sessionToken });
const { mutate: save } = useCacheSet();
const { mutate: clear } = useCacheDelete();
return (
<button onClick={() => save({ cache: 'app-cache', key: 'theme', value: 'dark', ttl: 86400, session: sessionToken })}>
Save theme
</button>
);
}Brokers
import { useBrokerPublish, useBrokerSubscription } from '@ductape/react';
function EventStream({ sessionToken }: { sessionToken: string }) {
const { mutate: publish } = useBrokerPublish();
useBrokerSubscription(
{ broker: 'kafka-cluster', topic: 'order-events', session: sessionToken },
(messages) => console.log(messages),
{ enabled: !!sessionToken }
);
return (
<button onClick={() => publish({ broker: 'kafka-cluster', topic: 'order-events', message: { type: 'ping' }, session: sessionToken })}>
Publish
</button>
);
}Vectors
import { useVectorQuery } from '@ductape/react';
function Search({ embedding, sessionToken }: { embedding: number[]; sessionToken: string }) {
const { data: results } = useVectorQuery(
['search', embedding],
{ vector: 'product-embeddings', query: embedding, topK: 10, session: sessionToken }
);
}Warehouse
import { useWarehouseQuery } from '@ductape/react';
function Report({ sessionToken }: { 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: sessionToken,
}
);
}Resilience
import { useQuotaCheck, useHealthStatus } from '@ductape/react';
function StatusPanel({ sessionToken }: { sessionToken: string }) {
const { data: quota } = useQuotaCheck({ tag: 'api-rate-limit', session: sessionToken });
const { data: health } = useHealthStatus({ tag: 'payment-service', session: sessionToken });
}Notifications
import { useEmailNotification, usePushNotification } from '@ductape/react';
function NotifyUser({ sessionToken }: { sessionToken: string }) {
const { mutate: sendEmail } = useEmailNotification();
return (
<button onClick={() => sendEmail({ notification: 'welcome-email', input: { email: '[email protected]' }, session: sessionToken })}>
Send welcome
</button>
);
}Analytics
import { useAnalytics } from '@ductape/react';
import { useEffect } from 'react';
function PageTracker({ sessionToken }: { sessionToken?: string }) {
const analytics = useAnalytics();
useEffect(() => {
if (sessionToken) analytics.identify(sessionToken);
analytics.track({ event: 'page_view', properties: { page: '/dashboard' } });
}, [sessionToken]);
}