Quickstart
This assumes you’ve completed Installation and have a stack
healthy at http://localhost:39001. We’ll create a project, a table, an RLS
policy, and read/write data — first with curl, then with the SDK.
Set a management token. The bootstrap admin credential for /api/v1/* is
ANVILBASE_ADMIN_TOKEN — it is the only token the management API accepts as a
root admin (CONTROL_PLANE_SECRET is a separate secret that only signs JWTs,
not a management token). anvilbase start generates ANVILBASE_ADMIN_TOKEN for
local stacks (and anvilbase status --local prints the matching
export ANVILBASE_TOKEN=<…> tip); for a raw docker compose up deployment you
set it yourself — add a line like
ANVILBASE_ADMIN_TOKEN=$(openssl rand -hex 32) to .env, re-run
docker compose up -d to apply it, then export the same value:
export ANVILBASE_TOKEN="$(grep '^ANVILBASE_ADMIN_TOKEN=' .env | cut -d= -f2)"# (Better: create a Personal Access Token — see the Management API reference.)1. Create a project
curl -s -X POST http://localhost:39001/api/v1/projects \ -H "Authorization: Bearer $ANVILBASE_TOKEN" \ -H "Content-Type: application/json" \ -d '{"name":"Quickstart"}' | tee project.json | jq .Capture the values you’ll reuse (the service_role_key and jwt_secret are shown
only once):
export PROJECT_ID=$(jq -r .id project.json)export ANON_KEY=$(jq -r .anon_key project.json)export SERVICE_KEY=$(jq -r .service_role_key project.json)echo "Project: $PROJECT_ID"2. Create a table
Tables are created on the management plane (RLS is on by default):
curl -s -X POST http://localhost:39001/api/v1/projects/$PROJECT_ID/schema/tables \ -H "Authorization: Bearer $ANVILBASE_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "name": "todos", "enable_rls": true, "columns": [ {"name":"id","type":"uuid","primary_key":true,"default":"gen_random_uuid()"}, {"name":"user_id","type":"uuid","nullable":false}, {"name":"task","type":"text","nullable":false}, {"name":"done","type":"boolean","default":"false"}, {"name":"created_at","type":"timestamptz","default":"now()"} ] }' | jq .3. Add an RLS policy
Right now the table has RLS enabled but no policies, so nothing is readable (fail-closed by design). Add a policy that lets authenticated users see and manage their own rows:
curl -s -X POST \ http://localhost:39001/api/v1/projects/$PROJECT_ID/rls/tables/todos/policies \ -H "Authorization: Bearer $ANVILBASE_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "name": "owner_all", "command": "ALL", "using_expression": "user_id = current_setting('\''app.current_user_id'\'')::uuid", "with_check_expression": "user_id = current_setting('\''app.current_user_id'\'')::uuid", "roles": ["authenticated"] }' | jq .You can verify a policy before trusting it with the RLS test endpoint, which runs a real query inside a transaction and rolls back:
curl -s -X POST http://localhost:39001/api/v1/projects/$PROJECT_ID/rls/test \ -H "Authorization: Bearer $ANVILBASE_TOKEN" \ -H "Content-Type: application/json" \ -d '{"table":"todos","role":"authenticated","user_id":"00000000-0000-0000-0000-000000000001"}' | jq .See Row Level Security for templates.
4. Write and read data
For local exploration we’ll use the service_role key (it bypasses RLS, so you
don’t need a signed-in user yet). In real client code you’d use the anon key
plus a user JWT.
# Insert a row (service_role bypasses RLS)curl -s -X POST "http://localhost:39001/v1/rest/$PROJECT_ID/todos" \ -H "apikey: $SERVICE_KEY" \ -H "Content-Type: application/json" \ -H "Prefer: return=representation" \ -d '{"user_id":"00000000-0000-0000-0000-000000000001","task":"Try AnvilBase"}' | jq .
# Read rows, filtered and ordered (PostgREST syntax)curl -s "http://localhost:39001/v1/rest/$PROJECT_ID/todos?done=eq.false&order=created_at.desc" \ -H "apikey: $SERVICE_KEY" | jq .That’s the whole loop: create table → policy → write → read. The query syntax
(done=eq.false, order=, select=, limit=…) is documented in
REST Query Syntax.
5. Same thing from the SDK
AnvilBase’s data plane is @supabase/supabase-js-compatible. The SDK base URL is
your host plus /v1/<project_id>:
import { createClient } from '@supabase/supabase-js'
const supabase = createClient( 'http://localhost:39001/v1/' + process.env.PROJECT_ID, process.env.ANON_KEY!, // anon key — safe in clients)
// Insertconst { data, error } = await supabase .from('todos') .insert({ user_id: userId, task: 'Try AnvilBase' }) .select()
// Readconst { data: todos } = await supabase .from('todos') .select('*') .eq('done', false) .order('created_at', { ascending: false })The full multi-language version (Python, Dart, raw fetch) is in Connect Your App.
6. Explore in the console
Open http://localhost:39004 and you’ll find the same project: a Table Editor, SQL Editor, RLS policy builder, Storage browser, Functions editor, and more. See the console tour in Self-Hosting → Overview.
Next steps
- Your First Project — keys, endpoints, and the lifecycle in depth.
- Connect Your App — wire a real frontend.
- Auth — add real users so the
anonkey + RLS does the work instead ofservice_role. - Realtime — subscribe to live changes on
todos.