client sdks

three first-party clients with the same surface across react, svelte, and vue. each manages the realtime websocket lifecycle for you — subscribe on first read, re-run on row changes, close when the last consumer drops. you don't talk to the WS directly.

key shape: the api key lives on the client. browser clients use a readonly-scope key (brk_); server SDKs / SSR use a developer-scope key. rotate via the api keys tab in your project — old keys revoke immediately.

@briven/react

react

reference client. `useQuery` re-runs on row changes via the realtime WS; `useMutation` is the same shape as TanStack Query so the api curve is short.

setup

import { BrivenProvider, createClient } from '@briven/react';

const client = createClient({
  url: 'https://api.briven.tech',
  projectId: 'p_xxx',
  apiKey: process.env.NEXT_PUBLIC_BRIVEN_KEY,
});

export function Providers({ children }: { children: React.ReactNode }) {
  return <BrivenProvider client={client}>{children}</BrivenProvider>;
}

use

'use client';
import { useQuery, useMutation } from '@briven/react';

export function Notes() {
  const { data, isLoading, error } = useQuery<'listNotes', { id: string; body: string }[]>(
    'listNotes',
    {},
  );
  const create = useMutation<'createNote', { body: string }, { id: string }>('createNote');

  if (isLoading) return <p>loading…</p>;
  if (error) return <p>{error.message}</p>;
  return (
    <>
      <button onClick={() => create.mutate({ body: 'hello' })}>add</button>
      <ul>{data?.map((n) => <li key={n.id}>{n.body}</li>)}</ul>
    </>
  );
}

note: useQuery accepts a stable args object — pass primitives, not new objects on every render, or the subscription identity will churn.

@briven/svelte

svelte 5

queries return a Readable store that fires while you're subscribed; the WS closes when the last consumer drops. svelte's reference-counted store contract does the lifecycle for us.

setup

import { setBrivenClient, createClient } from '@briven/svelte';

const client = createClient({
  url: 'https://api.briven.tech',
  projectId: 'p_xxx',
  apiKey: import.meta.env.VITE_BRIVEN_KEY,
});

setBrivenClient(client);

use

<script lang="ts">
  import { query, mutation } from '@briven/svelte';
  const notes = query<'listNotes', { id: string; body: string }[]>('listNotes', {});
  const create = mutation<'createNote', { body: string }, { id: string }>('createNote');
</script>

{#if $notes.isLoading}loading…{/if}
{#if $notes.error}{$notes.error.message}{/if}
<button onclick={() => create.mutate({ body: 'hello' })}>add</button>
<ul>
  {#each $notes.data ?? [] as n (n.id)}<li>{n.body}</li>{/each}
</ul>

@briven/vue

vue 3

composables version of the same surface. `useQuery` returns refs; `watch(stableKey(args))` re-subscribes when arg identity changes. `onScopeDispose` closes the sub on component unmount.

setup

// main.ts
import { createApp } from 'vue';
import { setBrivenClient, createClient } from '@briven/vue';

const client = createClient({
  url: 'https://api.briven.tech',
  projectId: 'p_xxx',
  apiKey: import.meta.env.VITE_BRIVEN_KEY,
});

setBrivenClient(client);
createApp(App).mount('#app');

use

<script setup lang="ts">
import { useQuery, useMutation } from '@briven/vue';
const { data, isLoading, error } = useQuery<'listNotes', { id: string; body: string }[]>(
  'listNotes',
  {},
);
const { mutate } = useMutation<'createNote', { body: string }, { id: string }>('createNote');
</script>

<template>
  <p v-if="isLoading">loading…</p>
  <p v-else-if="error">{{ error.message }}</p>
  <button @click="mutate({ body: 'hello' })">add</button>
  <ul><li v-for="n in data ?? []" :key="n.id">{{ n.body }}</li></ul>
</template>