Skip to content

Quickstart

In this guide you create a posts table that each user can only read and write for themselves, then use the SDK to sign a test user in and work with rows.

  • Node.js 22 or later.
  • A VAIF Studio account. Create one in the console.
  • The vaif CLI, signed in with vaif login. See Installation.

In the console, choose New project and give it a name.

Save this SQL in your app as migrations/0001_posts.sql:

-- vaif:owner author_id
CREATE TABLE posts (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
author_id uuid NOT NULL,
title text NOT NULL,
content text,
published boolean DEFAULT false,
created_at timestamptz DEFAULT now()
);

Then apply it to your project:

Terminal window
vaif db push --dir ./migrations --project-id <your-project-id>

The -- vaif:owner author_id line above CREATE TABLE adds a row rule: each signed-in user reads, changes and deletes only the posts whose author_id is their own user id, and the server fills author_id on every new post.

You can also create the table on the console’s Schema page. A table made there has no rule yet; add one as described in Row rules.

Open Security > Credentials in the console and copy the project ID and the publishable key (it starts with vk_pub_). Row rules apply to a publishable key, so it is safe in browser and app code. See API keys.

Install @vaifstudio/client in your app:

Terminal window
npm install @vaifstudio/client

The package works in browsers, Node.js 22 and later, React Native and edge runtimes. For other languages, see SDKs or call the REST API.

import { createVaifClient } from "@vaifstudio/client";
// Columns the server fills (id, created_at, and author_id from the row rule)
// are optional when you create a row.
type Post = {
id: string;
author_id?: string;
title: string;
content: string | null;
published: boolean;
created_at?: string;
};
let accessToken = "";
const vaif = createVaifClient({
baseUrl: "https://api.vaif.studio",
projectId: process.env.VAIF_PROJECT_ID,
apiKey: process.env.VAIF_PUBLISHABLE_KEY, // vk_pub_: row rules apply
accessToken: () => accessToken,
});
// Sign a test user in, so the row rule has someone to match.
const email = "sam@example.com";
const password = "a-long-unique-passphrase";
await vaif.auth.signUp(email, password).catch(() => undefined); // already signed up: fine
const result = await vaif.auth.login(email, password);
if ("accessToken" in result) accessToken = result.accessToken;
const posts = vaif.from<Post>("posts");
// Create a row
const { data: post } = await posts.create({ title: "My first post", content: "Hello from VAIF!", published: true });
console.log(post.id);
// Query with a filter, an order and a limit
const { data: published } = await posts
.eq("published", true)
.orderBy("created_at", "desc")
.limit(10)
.list();
console.log(published.length);

A query is built with chained calls and runs when you call a method such as list(), first() or get(id). See Querying data.

The script uses the publishable key, as browser and app code does. The row rule limits it to the signed-in user’s posts, and fills author_id for you. Tables are closed to requests with no signed-in user; give a table that visitors may read a public rule.