quickstart

from nothing to a live reactive query in five minutes.

  1. 1

    wire a cloud project

    Install the CLI (or use npx @briven/cli). Then pick one path:
    • new project: briven setup --name my-app (or interactive briven setup)
    • existing project: briven connect p_… — never setup --project
    Full detail: connect.
    $ mkdir my-app && cd my-app
    $ npx briven setup --name my-app
    dashboard-only: sign in at briven.tech, use studio, then copy as schema.ts when you want CLI + git.

    optional starter: --template=todo-app or --template=chat (templates are not the product — just files).

  2. 2

    edit the schema + function

    Open briven/schema.ts:

    import { schema, table, text, timestamp } from '@briven/schema';
    
    export default schema({
      notes: table({
        columns: {
          id: text().primaryKey(),
          body: text().notNull(),
          createdAt: timestamp().notNull().defaultNow(),
        },
        indexes: [{ columns: ['createdAt'] }],
      }),
    });

    Open briven/functions/notes.ts:

    import { mutation, query } from '@briven/cli/server';
    
    export const list = query({
      args: {},
      handler: async (ctx) => {
        return ctx.db('notes')
          .select(['id', 'body', 'createdAt'])
          .orderBy('createdAt', 'desc')
          .limit(100);
      },
    });
    
    export const create = mutation({
      args: { body: 'string' },
      handler: async (ctx, args) => {
        const id = crypto.randomUUID();
        await ctx.db('notes').insert({
          id,
          body: args.body,
          createdAt: new Date(),
        });
        return { id };
      },
    });
  3. 3

    deploy

    $ npx briven deploy

    The CLI prints a schema diff, applies it transactionally on the data plane, uploads the function bundle, and signals the runtime to swap in the new isolate. New row appears under deployments on the dashboard.

  4. 4

    wire it up from your app

    $ npm install @briven/react

    In your app (Next.js, Vite, Remix — anything React):

    // app/providers.tsx
    'use client';
    import { BrivenProvider, createClient } from '@briven/react';
    
    const client = createClient({
      projectId: 'p_01HZ...',
      apiKey: process.env.NEXT_PUBLIC_BRIVEN_PUBLIC_KEY!,  // 'brk_pub_...'
    });
    
    export function Providers({ children }: { children: React.ReactNode }) {
      return <BrivenProvider client={client}>{children}</BrivenProvider>;
    }
    
    // app/notes/page.tsx
    'use client';
    import { useMutation, useQuery } from '@briven/react';
    
    export default function Notes() {
      const { data, isLoading } = useQuery('notes:list', {});
      const create = useMutation('notes:create');
    
      if (isLoading) return <p>loading…</p>;
      return (
        <div>
          {data?.map(n => <p key={n.id}>{n.body}</p>)}
          <button onClick={() => create.mutate({ body: 'hello' })}>add</button>
        </div>
      );
    }

    On add, briven inserts the row, the realtime service picks up the postgres NOTIFY for the notes table, re-invokes every active notes:list subscription, and pushes the fresh result — no polling, no manual cache invalidation.

what just happened

  • briven deploy compiled your TypeScript schema to a postgres migration and ran it transactionally — if any column add failed, no rows changed.
  • Your function bundle was uploaded to the runtime and assigned a fresh Deno isolate. Cold start budget: 200ms p50.
  • On every useQuery on the client, briven's realtime service registered a LISTEN on every postgres table the function read — so any subsequent mutation that touches those tables triggers an automatic re-invoke.

what to read next