quickstart
from nothing to a live reactive query in five minutes.
- 1
wire a cloud project
Install the CLI (or usenpx @briven/cli). Then pick one path:- new project:
briven setup --name my-app(or interactivebriven setup) - existing project:
briven connect p_…— neversetup --project
$ 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-appor--template=chat(templates are not the product — just files). - new project:
- 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
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
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
notestable, re-invokes every activenotes:listsubscription, and pushes the fresh result — no polling, no manual cache invalidation.
what just happened
briven deploycompiled 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
useQueryon 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
schema
every column type, constraint, index, default, and diff semantic
functions
query/mutation/action wrappers, Ctx, db builder, lifecycle, error handling
cli
every command — deploy, dev (watch + hot reload), env, db, logs, invoke, projects
migration
moving from convex, supabase, postgres/drizzle/prisma, firebase, hasura, nextauth