SDK Connection Guide
This guide shows how to connect to your self-hosted WoWSQL instance from JavaScript, Python, and other languages using the official SDK or raw HTTP.
Getting Your Credentials
You need two values to connect:
- API URL — The Kong gateway address (default:
http://localhost:8080) - Anon Key — The public API key (typically
wowsql_anon_...)
Both are available in the dashboard under Settings → API. REST / Auth / Storage send the key as the apikey HTTP header. Realtime WebSockets use the same key as ?apikey= on /realtime/v1/websocket (browsers cannot set WebSocket headers). Missing key closes 4001; invalid key closes 4003.
wowsql_service_...) on the server only — it bypasses Row Level Security.JavaScript / TypeScript
Install
npm install @wowsql/sdk
Initialize
import WowSQLClient from '@wowsql/sdk'
import { ProjectAuthClient, WowSQLStorage } from '@wowsql/sdk'
const client = new WowSQLClient({
projectUrl: 'http://localhost:8080', // Kong gateway
apiKey: 'YOUR_ANON_KEY', // wowsql_anon_...
secure: false // http:// local
})
Query data
const { data } = await client.table('todos').select('*').get()
const created = await client.table('todos').insert({
title: 'Buy groceries',
completed: false
})
await client.table('todos').update(created.id, { completed: true })
await client.table('todos').delete(created.id)
Authentication
const auth = new ProjectAuthClient({
projectUrl: 'http://localhost:8080',
apiKey: 'YOUR_ANON_KEY',
secure: false
})
const { session, user } = await auth.signUp({
email: 'user@example.com',
password: 'secure-password'
})
const signedIn = await auth.signIn({
email: 'user@example.com',
password: 'secure-password'
})
// CAPTCHA — only when Attack Protection → Turnstile is enabled with your own widget keys
await auth.signIn({
email: 'user@example.com',
password: 'secure-password',
captchaToken: turnstileToken
})
const me = await auth.getUser(signedIn.session.accessToken)
await auth.logout(signedIn.session.accessToken)
Realtime
Connects to ws://localhost:8080/realtime/v1/websocket?apikey=YOUR_ANON_KEY. Enable the table first with POST /realtime/v1/enable. Channel send / presence do not need a Postgres trigger. Payloads are capped at 64 KiB.
const channel = client.realtime.channel('todos-changes')
channel
.on('postgres_changes', { event: '*', schema: 'public', table: 'todos' }, (payload) => {
console.log('Change:', payload.new || payload.old)
})
.on('broadcast', { event: 'typing' }, ({ payload }) => {
console.log('typing', payload)
})
.subscribe()
await channel.send({ type: 'broadcast', event: 'typing', payload: { user: 'Alice' } })
Storage
const storage = new WowSQLStorage({
projectUrl: 'http://localhost:8080',
apiKey: 'YOUR_ANON_KEY',
secure: false
})
await storage.upload('avatars', file, 'user1.png')
const url = storage.getPublicUrl('avatars', 'user1.png')
Python
Install
pip install wowsql
Initialize
from wowsql import WowSQLClient, ProjectAuthClient, WowSQLStorage
client = WowSQLClient(
project_url="http://localhost:8080",
api_key="YOUR_ANON_KEY",
secure=False,
)
Query data
result = client.table("todos").select("*").get()
print(result["data"])
created = client.table("todos").insert({
"title": "Buy groceries",
"completed": False,
})
client.table("todos").update(created["id"], {"completed": True})
client.table("todos").delete(created["id"])
Authentication
auth = ProjectAuthClient(
project_url="http://localhost:8080",
api_key="YOUR_ANON_KEY",
secure=False,
)
signed_up = auth.sign_up(email="user@example.com", password="secure-password")
signed_in = auth.sign_in(email="user@example.com", password="secure-password")
user = auth.get_user(signed_in.session.access_token)
auth.logout(signed_in.session.access_token)
Realtime
def on_change(change):
print(change["event"], change.get("new") or change.get("old"))
sub = client.realtime.subscribe("todos", on_change, schema="public", event="*")
channel = client.realtime.channel("chat")
channel.on("broadcast", {"event": "typing"}, lambda msg: print(msg["payload"]))
channel.subscribe()
channel.send(event="typing", payload={"user": "Alice"})
Raw HTTP (cURL)
You can use any HTTP client. Pass the anon key in both headers:
Read data
curl 'http://localhost:8080/rest/v1/todos?select=*' \
-H "apikey: YOUR_ANON_KEY" \
-H "Authorization: Bearer YOUR_ANON_KEY"
Insert data
curl 'http://localhost:8080/rest/v1/todos' \
-X POST \
-H "apikey: YOUR_ANON_KEY" \
-H "Authorization: Bearer YOUR_ANON_KEY" \
-H "Content-Type: application/json" \
-H "Prefer: return=representation" \
-d '{"title": "Buy groceries", "completed": false}'
Filter and paginate
# Filter: completed = true
curl 'http://localhost:8080/rest/v1/todos?completed=eq.true' \
-H "apikey: YOUR_ANON_KEY" \
-H "Authorization: Bearer YOUR_ANON_KEY"
# Paginate: 10 items starting from offset 20
curl 'http://localhost:8080/rest/v1/todos?select=*&limit=10&offset=20' \
-H "apikey: YOUR_ANON_KEY" \
-H "Authorization: Bearer YOUR_ANON_KEY"
# Order by created_at descending
curl 'http://localhost:8080/rest/v1/todos?order=created_at.desc' \
-H "apikey: YOUR_ANON_KEY" \
-H "Authorization: Bearer YOUR_ANON_KEY"
Enable a table for realtime
curl 'http://localhost:8080/realtime/v1/enable' \
-X POST \
-H "apikey: YOUR_ANON_KEY" \
-H "Content-Type: application/json" \
-d '{"schema_name":"public","table_name":"todos"}'
Then connect a WebSocket to ws://localhost:8080/realtime/v1/websocket?apikey=YOUR_ANON_KEY. Send {"type":"subscribe","schema":"public","table":"todos","event":"*"} for postgres changes, or {"type":"join","channel":"chat"} plus {"type":"broadcast","channel":"chat","event":"typing","payload":{}} for ephemeral events.
Authenticate a user
# Sign up
curl 'http://localhost:8080/auth/v1/signup' \
-X POST \
-H "apikey: YOUR_ANON_KEY" \
-H "Content-Type: application/json" \
-d '{"email": "user@example.com", "password": "securepass"}'
# Sign in (returns access_token)
curl 'http://localhost:8080/auth/v1/token?grant_type=password' \
-X POST \
-H "apikey: YOUR_ANON_KEY" \
-H "Content-Type: application/json" \
-d '{"email": "user@example.com", "password": "securepass"}'
Use authenticated token
After signing in, use the returned access_token in subsequent requests:
curl 'http://localhost:8080/rest/v1/todos' \
-H "apikey: YOUR_ANON_KEY" \
-H "Authorization: Bearer USER_ACCESS_TOKEN"
apikey header always uses the anon key. The Authorization header uses the user's access token after login. This tells PostgREST to execute queries as the authenticated role.Using the Service Role Key
The service role key bypasses Row Level Security. Use it only in server-side code (backend APIs, scripts, admin tools).
// Server-side only
import WowSQLClient from '@wowsql/sdk'
const admin = new WowSQLClient({
projectUrl: 'http://localhost:8080',
apiKey: 'YOUR_SERVICE_ROLE_KEY',
secure: false
})
const { data } = await admin.table('todos').select('*').get()
Row Level Security Basics
After creating a table, enable RLS and write policies to control access:
-- Enable RLS on the table
ALTER TABLE public.todos ENABLE ROW LEVEL SECURITY;
-- Allow anyone to read all todos
CREATE POLICY "Public read access"
ON public.todos FOR SELECT
USING (true);
-- Only the owner can insert
CREATE POLICY "Users insert own todos"
ON public.todos FOR INSERT
WITH CHECK (auth.uid() = user_id);
-- Only the owner can update
CREATE POLICY "Users update own todos"
ON public.todos FOR UPDATE
USING (auth.uid() = user_id);
-- Only the owner can delete
CREATE POLICY "Users delete own todos"
ON public.todos FOR DELETE
USING (auth.uid() = user_id);