Skip to main content
The Database primitive is managed Postgres, backed by a fullstack app’s Supabase project. Naive owns the credentials and injects them — you run SQL, read/write rows through PostgREST, and apply migrations without ever holding a Supabase key.
Database (and Storage, Edge Functions, Auth) operate on a fullstack app’s Supabase project. Create one first: naive.apps.create({ name, type: "fullstack" }). These are curated surfaces over the same scoped Supabase proxy that powers the apps primitive.

Two ways to use it

Database works for your own project and for each of your end-users’ projects, using Naive’s multi-tenancy:
import { Naive } from "@usenaive-sdk/node";
const naive = new Naive({ apiKey: process.env.NAIVE_API_KEY! });

// 1. Your own project — the account's default user
await naive.database.query("select now()");

// 2. A specific end-user's project (multi-tenant SaaS)
const alice = await naive.users.create({ external_id: "alice" });
await naive.apps.create({ name: "Alice Workspace", type: "fullstack" }); // default user
await naive.forUser(alice.id).apps.create({ name: "Alice App", type: "fullstack" });
await naive.forUser(alice.id).database.query("select count(*) from profiles");
When a user owns exactly one fullstack app, the call auto-resolves it. With multiple apps, pass { appId }.

SQL

// Arbitrary SQL — SELECT, INSERT/UPDATE/DELETE, and DDL
await naive.database.query("create table notes (id serial primary key, body text)");
const rows = await naive.database.query("select * from notes order by id desc limit 10");

// Target a specific app when the user has several
await naive.database.query("select 1", { appId: "app-uuid" });

// List tables with row counts
const { tables } = await naive.database.tables();

PostgREST CRUD

const notes = naive.database.from("notes");
await notes.insert({ body: "hello" });
await notes.select("id,body", "order=id.desc&limit=5");
await notes.update({ body: "edited" }, "id=eq.1");
await notes.delete("id=eq.1");
Filters use PostgREST query syntax. Runs with the service-role key (RLS bypassed) — treat it as admin access.

Schema changes

await naive.database.migrate(
  "alter table notes add column created_at timestamptz default now()",
);
migrate runs DDL against the database (works on any plan). Supabase’s tracked database/migrations endpoint is gated to approved orgs — reach it directly through the Supabase proxy (.../supabase/proxy/v1/projects/{ref}/database/migrations) where your org has access.

REST

# SQL
curl -X POST https://api.usenaive.ai/v1/apps/<app-id>/db/query \
  -H "Authorization: Bearer nv_sk_live_..." -H "Content-Type: application/json" \
  -d '{"sql": "select now()"}'

# PostgREST
curl "https://api.usenaive.ai/v1/apps/<app-id>/db/rest/notes?select=*" \
  -H "Authorization: Bearer nv_sk_live_..."
Your own project is reached at /v1/apps/<app-id>/…; an end-user’s project is /v1/users/<user-id>/apps/<app-id>/…. The SDK picks the right path automatically (naive.database vs naive.forUser(id).database). See the Database API reference.

Billing

Free — no per-operation credit cost. The underlying Supabase project comes with the fullstack app. Account Kits can gate the database primitive per user.