Connect Your App
AnvilBase’s data plane is @supabase/supabase-js-compatible, so the official
Supabase clients in several languages work against it. This page shows how to
connect from TypeScript, Python, Dart/Flutter, and plain HTTP.
You need two things from Your First Project:
- the project URL:
http://<host>/v1/<project_id>(e.g.http://localhost:39001/v1/3f2b…) - the anon key (
anvilbase_anon_<slug>_…) for client code, or the service_role key for trusted server code.
Key choice. Use the anon key in anything a user can see (browser, mobile, public CLI) — RLS protects your data. Use the service_role key only in server-side code; it bypasses RLS. See API Keys & Scopes.
TypeScript / JavaScript (@supabase/supabase-js)
npm install @supabase/supabase-jsimport { createClient } from '@supabase/supabase-js'
const ANVILBASE_URL = 'http://localhost:39001/v1/<project_id>'const ANON_KEY = 'anvilbase_anon_my-app_…'
export const db = createClient(ANVILBASE_URL, ANON_KEY)
// READ — filter, order, paginateconst { data: todos, error } = await db .from('todos') .select('id, task, done, created_at') .eq('done', false) .order('created_at', { ascending: false }) .range(0, 19) // first 20 rows
// INSERTawait db.from('todos').insert({ task: 'Ship it', user_id: userId })
// UPDATEawait db.from('todos').update({ done: true }).eq('id', todoId)
// DELETEawait db.from('todos').delete().eq('id', todoId)
// UPSERTawait db.from('todos').upsert({ id: todoId, task: 'Revised' })
// CALL a Postgres function (RPC)const { data } = await db.rpc('search_todos', { q: 'ship' })Using a signed-in user’s JWT
For server-side or non-SDK callers that already hold a project JWT (e.g.
minted via the raw token endpoint), pass it so requests run as authenticated
and RLS scopes to that user. (In the browser, prefer
db.auth.signInWithPassword() below — the SDK attaches the header for you.)
const db = createClient(ANVILBASE_URL, ANON_KEY, { global: { headers: { Authorization: `Bearer ${userJwt}` } },})Realtime
const channel = db .channel('todos-changes') .on('postgres_changes', { event: '*', schema: 'public', table: 'todos' }, (payload) => console.log('change:', payload) ) .subscribe()See Realtime → Postgres Changes.
Storage
// Uploadawait db.storage.from('avatars').upload(`${userId}/photo.jpg`, file)// Public/Signed URLconst { data } = await db.storage.from('avatars') .createSignedUrl(`${userId}/photo.jpg`, 3600)See Storage → Working with Objects.
Python (supabase-py)
pip install supabasefrom supabase import create_client, Client
ANVILBASE_URL = "http://localhost:39001/v1/<project_id>"ANON_KEY = "anvilbase_anon_my-app_…"
db: Client = create_client(ANVILBASE_URL, ANON_KEY)
# READres = ( db.table("todos") .select("id, task, done") .eq("done", False) .order("created_at", desc=True) .limit(20) .execute())todos = res.data
# INSERTdb.table("todos").insert({"task": "Ship it", "user_id": user_id}).execute()
# UPDATEdb.table("todos").update({"done": True}).eq("id", todo_id).execute()
# DELETEdb.table("todos").delete().eq("id", todo_id).execute()
# RPCdb.rpc("search_todos", {"q": "ship"}).execute()For trusted server jobs, pass the service_role key instead of the anon key to bypass RLS.
Dart / Flutter (supabase_flutter)
dependencies: supabase_flutter: ^2.0.0import 'package:supabase_flutter/supabase_flutter.dart';
await Supabase.initialize( url: 'http://10.0.2.2:39001/v1/<project_id>', // 10.0.2.2 = host from Android emulator anonKey: 'anvilbase_anon_my-app_…',);
final db = Supabase.instance.client;
// READfinal todos = await db .from('todos') .select() .eq('done', false) .order('created_at', ascending: false) .limit(20);
// INSERTawait db.from('todos').insert({'task': 'Ship it', 'user_id': userId});
// UPDATE / DELETEawait db.from('todos').update({'done': true}).eq('id', todoId);await db.from('todos').delete().eq('id', todoId);
// Realtimedb.channel('todos').onPostgresChanges( event: PostgresChangeEvent.all, schema: 'public', table: 'todos', callback: (payload) => debugPrint('$payload'),).subscribe();Raw HTTP (any language)
The data plane is plain REST. Two headers matter: apikey (your project key) and,
for a signed-in user, Authorization: Bearer <jwt>.
BASE="http://localhost:39001/v1/rest/<project_id>"
# READcurl -s "$BASE/todos?done=eq.false&order=created_at.desc&limit=20" \ -H "apikey: $ANON_KEY"
# INSERT (return the inserted row)curl -s -X POST "$BASE/todos" \ -H "apikey: $ANON_KEY" -H "Content-Type: application/json" \ -H "Prefer: return=representation" \ -d '{"task":"Ship it","user_id":"…"}'
# UPDATEcurl -s -X PATCH "$BASE/todos?id=eq.$ID" \ -H "apikey: $SERVICE_KEY" -H "Content-Type: application/json" \ -d '{"done":true}'
# DELETEcurl -s -X DELETE "$BASE/todos?id=eq.$ID" -H "apikey: $SERVICE_KEY"
# RPCcurl -s -X POST "$BASE/rpc/search_todos" \ -H "apikey: $ANON_KEY" -H "Content-Type: application/json" \ -d '{"q":"ship"}'Go, Rust, Java, etc. work the same way — point any HTTP client at /v1/rest/....
Auth is drop-in too
The SDK’s auth module works unmodified — AnvilBase serves a
GoTrue-compatible facade at the same paths supabase-js calls, so you use
db.auth.* on the same createClient instance:
// same db from "Connecting" above — no separate client, no manual fetchawait db.auth.signUp({ email, password })const { data } = await db.auth.signInWithPassword({ email, password })// db is now authenticated; the SDK sends the Bearer token on every request.
await db.auth.signInWithOtp({ email }) // magic-link / email OTPawait db.auth.getUser()await db.auth.signOut()You do not need to fetch a JWT by hand or inject Authorization yourself for
SDK flows — the “Using a signed-in user’s JWT” block above is only for
server-side / non-SDK callers that already hold a token.
Auth compatibility notes. The standard flows are drop-in, and the service-role
supabase.auth.admin.*user-management surface is served too (it requires the project service_role key). One edge to know: a user created via OTP / magic-link sign-in does not capture signup-timeoptions.datametadata at first verify — password and anonymous signup persist it immediately; for OTP/magic-link, callupdateUser({ data })after the first sign-in. See Auth → Overview.
Full endpoint list and the compatibility matrix: Auth → Overview and Tools → SDK Compatibility.
The reference app
examples/todo-app/ in the repo is a complete CRUD + realtime + storage demo
wired against the SDK — the fastest way to see a working end-to-end integration.
Next: Database → Overview.