Skip to content

Use VAIF with AI app builders

AI app builders such as Lovable and v0, and coding agents in editors such as Cursor, can build a frontend on VAIF from these docs and the SDK. They tend to get the same few things wrong unless they are told, so this page lists them and ends with a prompt you can paste into the tool.

Key Prefix Where it goes
Publishable key vk_pub_ The frontend. Row rules apply to it, so it is safe in a browser bundle
Secret key vk_srv_ Never the frontend. It bypasses row rules. Only your own server or CI uses it, and the API refuses it from browser origins

Both are on Security > Credentials, next to the project ID. A frontend needs only the project ID and the publishable key. Signed-in users get their own access token from the SDK.

Use these names everywhere, so the tool, the docs and your teammates agree:

Tool What it builds Variables
Lovable Vite and React apps VITE_VAIF_PROJECT_ID, VITE_VAIF_PUBLISHABLE_KEY
v0 Next.js apps NEXT_PUBLIC_VAIF_PROJECT_ID, NEXT_PUBLIC_VAIF_PUBLISHABLE_KEY
Coding agents in your editor Whatever your project uses The Vite or Next.js names above for a frontend; VAIF_PROJECT_ID and VAIF_PUBLISHABLE_KEY (and VAIF_SECRET_KEY, on a server only) for Node.js
import type { ReactNode } from "react";
import { createVaifClient } from "@vaifstudio/client";
import { VaifProvider } from "@vaifstudio/react";
const vaif = createVaifClient({
baseUrl: "https://api.vaif.studio",
projectId: import.meta.env.VITE_VAIF_PROJECT_ID,
apiKey: import.meta.env.VITE_VAIF_PUBLISHABLE_KEY, // vk_pub_: never the secret key
});
export function Providers({ children }: { children: ReactNode }) {
return <VaifProvider client={vaif}>{children}</VaifProvider>;
}

In a browser with a publishable key, the SDK keeps the user signed in by itself: the refresh token travels in the response body and is replaced on every refresh. The app stores no tokens of its own. See Session management.

A table with no rules at all is open to every signed-in user, for reading and writing: any user of the app can read and change every row. Requests with no signed-in user are denied unless the table has a public rule. So for every table the tool creates:

  • a claim rule for a table that holds a user’s data. In a SQL file, put -- vaif:owner author_id on the line above CREATE TABLE;
  • a public rule, usually for reading only, for what signed-out visitors may see. In a SQL file, put -- vaif:public-read above CREATE TABLE.

With a claim rule that covers inserts, the server fills the owner column from the signed-in user: the app sends a post without author_id, and a row that names someone else is refused. See Row rules.

Tables are created with a SQL migration file and vaif db push, or on the console’s Schema page. The SQL editor cannot run CREATE TABLE.

Columns are whatever the SQL says, which is usually snake_case: created_at, author_id, user_id. Tools trained on other SDKs often write createdAt. VAIF does not rename columns, so createdAt names a column that does not exist. Write the row types with the real names, or generate them with vaif generate.

With @vaifstudio/react, query options may be written inline in the component. A query runs again only when what the options say changes, not on every render.

import { useQuery } from "@vaifstudio/react";
type Post = { id: string; title: string; author_id: string; created_at: string };
export function MyPosts() {
const { data, isLoading } = useQuery<Post>("posts", {
orderBy: { field: "created_at", direction: "desc" },
limit: 20,
});
if (isLoading) return <p>Loading…</p>;
return <ul>{data.map((p) => <li key={p.id}>{p.title}</li>)}</ul>;
}

Social sign-in and magic links send the user back to a page of your app, and that page’s origin must be on the project’s allowed redirect URLs (Authentication > URL configuration). Matching is by exact origin, with no wildcards. A preview deploy usually has an origin of its own, so add each one you test sign-in on, plus http://localhost:5173 or http://localhost:3000 for local development.

This app uses VAIF Studio as its backend, with @vaifstudio/client and @vaifstudio/react.
Keys and config
- The frontend uses ONLY the project ID and the PUBLISHABLE key (starts with vk_pub_).
Never put the secret key (vk_srv_) in frontend code, env files the browser reads, or the repo.
- Vite: import.meta.env.VITE_VAIF_PROJECT_ID and import.meta.env.VITE_VAIF_PUBLISHABLE_KEY.
Next.js: process.env.NEXT_PUBLIC_VAIF_PROJECT_ID and process.env.NEXT_PUBLIC_VAIF_PUBLISHABLE_KEY.
- Create one client with createVaifClient({ baseUrl: "https://api.vaif.studio", projectId, apiKey })
and wrap the app in <VaifProvider client={vaif}>. Use useAuth() for sign-up, sign-in and sign-out.
Do not store tokens yourself; the SDK keeps the session.
Data
- Column names are exactly the table's (snake_case: created_at, author_id). Never camelCase them.
- Every table that holds user data has an owner column (for example author_id uuid NOT NULL)
and the line "-- vaif:owner author_id" directly above its CREATE TABLE.
A table with no rule is open to every signed-in user.
- Do NOT send the owner column on insert: the server fills it from the signed-in user.
- Only tables signed-out visitors may read get "-- vaif:public-read" above their CREATE TABLE.
- Create tables with SQL migration files in ./migrations and `vaif db push`, not the SQL editor.
- useQuery and useCount options can be inline objects. Filter with
where: { field: "status", operator: "eq", value: "active" }; order with
orderBy: { field: "created_at", direction: "desc" }.
Sign-in redirects
- Social sign-in: useOAuth().signInWithOAuth("google" or "github", redirectUrl). On the return page,
call client.auth.handleOAuthCallback() and pass the result to setSession() from useVaif().
- Every page users return to must have its exact origin on the project's allowed redirect URLs,
including each preview deploy and localhost.