quickstart

from nothing to a live reactive query in five minutes.

  1. 1

    create a project in the dashboard

    Sign in at briven.tech, click new project, copy the resulting project id (looks like p_01HZ…).
    dashboard-only path:if you want to avoid the CLI entirely, open the project's studio tab and click + new table — define columns, set PKs/FKs/indexes, then insert rows and run queries from the SQL editor. The copy as schema.tsbutton graduates you to CLI + git when you're ready. The steps below describe the CLI flow.
  2. 2

    generate an api key

    Open api keys on the new project and create one — pick the developer role for local iteration. The plaintext (brk_…) is shown once; store it in a secret manager immediately.
  3. 3

    scaffold locally

    $ mkdir my-app && cd my-app
    $ npm install @briven/cli @briven/schema
    $ npx briven init
    $ npx briven login --project p_xxx --key brk_xxx

    briven init drops three files: briven.config.ts, briven/schema.ts, and briven/functions/notes.ts. The default template is a minimal notes app you can deploy as-is. Use briven init --template=todo-app or --template=chat for richer starting points.

  4. 4

    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 };
      },
    });
  5. 5

    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.

  6. 6

    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