Skip to main content

WowSQL Python SDK

Official Python client for WowSQL. All data operations communicate directly with PostgREST over HTTPS. No legacy API layers.

Requirements

  • Python 3.8 or later
  • requests and websocket-client

Installation

pip install wowsql

Quick Start

from wowsql import WowSQLClient

client = WowSQLClient(
    project_url="myproject",        # slug, domain, or full URL
    api_key="wowsql_anon_...",      # anon or service_role key
)

result = client.table("users").get()
print(result["data"])

Configuration

Parameter Type Default Description
project_url str required Project slug, domain, or full URL
api_key str required Anonymous or service role API key
base_domain str wowsqlconnect.com Base domain when project_url is a slug
secure bool True Use HTTPS
timeout int 30 Request timeout in seconds
verify_ssl bool True Verify SSL certificates

project_url formats

# Slug only
WowSQLClient(project_url="myproject", api_key="...")

# Full domain
WowSQLClient(project_url="myproject.wowsqlconnect.com", api_key="...")

# Full URL
WowSQLClient(project_url="https://myproject.wowsqlconnect.com", api_key="...")

API Keys

Key Type Prefix Usage
Anonymous wowsql_anon_... Client-side, public access with RLS
Service Role wowsql_service_... Server-side, full access, bypasses RLS

Database Operations

Querying Records

# Get all records
result = client.table("products").get()
data = result["data"]  # list of records
total = result["total"]  # total count

# Select specific columns
result = client.table("products").select("id", "name", "price").get()

# Filter with equality
result = client.table("products").eq("category", "electronics").get()

# Multiple filters (all ANDed by default)
result = (
    client.table("products")
    .gte("price", 100)
    .lte("price", 500)
    .eq("in_stock", True)
    .get()
)

# OR filter
result = (
    client.table("products")
    .eq("status", "active")
    .or_("status", "eq", "featured")
    .get()
)

Filter Operators

Operator Method Description
Equal .eq(col, val) column = value
Not equal .neq(col, val) column != value
Greater than .gt(col, val) column > value
Greater or equal .gte(col, val) column >= value
Less than .lt(col, val) column < value
Less or equal .lte(col, val) column <= value
LIKE .like(col, pattern) case-sensitive pattern match
ILIKE .ilike(col, pattern) case-insensitive pattern match
Is null .is_null(col) column IS NULL
Is not null .is_not_null(col) column IS NOT NULL
In list .in_(col, [vals]) column IN (...)
Not in list .not_in(col, [vals]) column NOT IN (...)
Between .between(col, lo, hi) lo <= column <= hi

Ordering and Pagination

# Order by column
result = client.table("products").order_by("price", "desc").get()

# Multiple column ordering
result = client.table("products").order_by([
    ("category", "asc"),
    ("price", "desc"),
]).get()

# Limit and offset
result = client.table("products").limit(20).offset(40).get()

# Paginate
result = client.table("products").paginate(page=2, per_page=20)
# result["data"], result["page"], result["per_page"]
# result["total"], result["total_pages"]

Single Record Fetch

# By primary key
user = client.table("users").get_by_id("uuid-here")

# First matching record
user = client.table("users").eq("email", "user@example.com").first()

# Exactly one record — raises WowSQLError if 0 or more than 1
user = client.table("users").eq("email", "user@example.com").single()

Creating Records

# Single record
response = client.table("users").create({
    "email": "new@example.com",
    "name": "New User",
})
new_id = response["id"]

# Alias
response = client.table("users").insert({"email": "new@example.com"})

# Bulk insert
responses = client.table("users").bulk_insert([
    {"email": "a@example.com", "name": "Alice"},
    {"email": "b@example.com", "name": "Bob"},
])

Updating Records

# Update by primary key
response = client.table("users").update("user-uuid", {"name": "Updated Name"})
print(response["affected_rows"])

Deleting Records

# Delete by primary key
response = client.table("users").delete("user-uuid")
print(response["affected_rows"])

Upsert

response = client.table("users").upsert(
    {"id": "existing-uuid", "name": "Updated"},
    on_conflict="id",  # default: "id"
)

Aggregates

# Count
total = client.table("orders").eq("status", "active").count()

# Sum
revenue = client.table("orders").eq("status", "paid").sum("amount")

# Average
avg_price = client.table("products").eq("category", "electronics").avg("price")

Group By

result = (
    client.table("orders")
    .select("status", "COUNT(*) as count")
    .group_by("status")
    .get()
)

Context Manager

with WowSQLClient(project_url="myproject", api_key="...") as client:
    result = client.table("users").get()

Authentication

from wowsql.auth import ProjectAuthClient

auth = ProjectAuthClient(
    project_url="myproject",
    api_key="wowsql_anon_...",
)

Email / Password

# Sign up
response = auth.sign_up(email="user@example.com", password="secure-password")
session = response.session
user = response.user

# Sign in
response = auth.sign_in(email="user@example.com", password="secure-password")

# Get current user
user = auth.get_user(access_token=session.access_token)

# Sign out
auth.logout(access_token=session.access_token)

OAuth

# Step 1 — get the redirect URL
result = auth.get_oauth_authorization_url(
    provider="google",
    redirect_uri="https://myapp.com/auth/callback",
)
# Redirect user to: result["authorization_url"]

# Step 2 — after OAuth callback, exchange code for tokens
response = auth.exchange_oauth_callback(
    provider="google",
    code=request.args["code"],
)
session = response.session
user = response.user

Token Management

# Refresh tokens
response = auth.refresh_token(refresh_token=session.refresh_token)

# Password reset
auth.forgot_password(email="user@example.com")
auth.reset_password(token="reset-token", new_password="new-password")

OTP and Magic Links

# OTP login
auth.send_otp(email="user@example.com")
response = auth.verify_otp(email="user@example.com", otp="123456")

# Magic link
auth.send_magic_link(email="user@example.com")

File Storage

from wowsql.storage import WowSQLStorage

storage = WowSQLStorage(
    project_url="myproject",
    api_key="wowsql_anon_...",
)

# Create bucket
storage.create_bucket("avatars", public=True)

# Upload file
with open("photo.jpg", "rb") as f:
    result = storage.upload("avatars", f, path="users/profile.jpg")

# Get public URL
url = storage.get_public_url("avatars", "users/profile.jpg")

# List files
files = storage.list_files("avatars", prefix="users/")

# Download
data = storage.download("avatars", "users/profile.jpg")

# Delete
storage.delete_file("avatars", "users/profile.jpg")

Schema Management

Requires a service role key.

from wowsql.schema import WowSQLSchema

schema = WowSQLSchema(
    project_url="myproject",
    service_key="wowsql_service_...",
)

# Create table
schema.create_table("products", [
    {"name": "id",         "type": "UUID",         "auto_increment": True},
    {"name": "name",       "type": "VARCHAR(255)",  "nullable": False},
    {"name": "price",      "type": "DECIMAL(10,2)", "nullable": False},
    {"name": "metadata",   "type": "JSONB",         "default": "'{}'"},
    {"name": "created_at", "type": "TIMESTAMPTZ",   "default": "CURRENT_TIMESTAMP"},
], primary_key="id", indexes=["name"])

# Add column
schema.add_column("products", "sku", "VARCHAR(100)")

# Drop column
schema.drop_column("products", "old_column")

# List tables
tables = schema.list_tables()

# Get table schema
info = schema.get_table_schema("products")

# Execute raw SQL (DDL only)
schema.execute_sql("CREATE INDEX ON products(name)")

# Drop table
schema.drop_table("products")

Realtime

Subscribe to INSERT / UPDATE / DELETE, broadcast ephemeral events, and track presence. Uses the same anon or service_role key as REST.

The SDK connects to:

wss://<project>.wowsqlconnect.com/realtime/v1/websocket?apikey=<wowsql_anon_... or wowsql_service_...>

Enable the table first (POST /realtime/v1/enable). Channel send / presence do not need a Postgres trigger. Payloads are capped at 64 KiB. Missing key closes 4001; invalid key closes 4003. Requires websocket-client (installed with the package).

from wowsql import WowSQLClient

client = WowSQLClient(project_url="myproject", api_key="wowsql_anon_...")

def on_change(change):
    print(change["event"], change.get("new") or change.get("old"))

sub = client.realtime.subscribe("messages", on_change, schema="public", event="*")

channel = client.realtime.channel("chat")
channel.on("broadcast", {"event": "typing"}, lambda msg: print("typing", msg["payload"]))
channel.on("presence", {}, lambda msg: print("online", channel.presence_state()))
channel.subscribe()
channel.track({"user": "alice"})
channel.send(event="typing", payload={"user": "alice"})

# later
sub["unsubscribe"]()
channel.unsubscribe()
client.close()

Error Handling

from wowsql import WowSQLError

try:
    result = client.table("orders").eq("id", "uuid").get()
except WowSQLError as err:
    print(err)             # Human-readable message
    print(err.status_code) # HTTP status code
    print(err.response)    # Raw PostgREST error dict

Architecture

All requests go directly to the PostgREST endpoint (/rest/v1). SDK filter expressions are translated into PostgREST query parameters. Pagination counts are read from the Content-Range response header.

Operation HTTP Method PostgREST Path
GET GET /{table}?col=op.val
CREATE POST /{table} with Prefer: return=representation
UPDATE PATCH /{table}?id=eq.{id}
DELETE DELETE /{table}?id=eq.{id}
UPSERT POST /{table} with Prefer: resolution=merge-duplicates
COUNT GET /{table}?limit=0 with Prefer: count=exact

License

MIT

Metadata

Release files for wowsql 3.9.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for wowsql 3.9.0
File Size Uploaded
wowsql-3.9.0.tar.gz 31.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for wowsql 3.9.0
File Interpreter ABI Platform
wowsql-3.9.0-py3-none-any.whl Python 3 none any Details

Total release size: 63.2 kB

Release files / wowsql-3.9.0.tar.gz

Download URL wowsql-3.9.0.tar.gz
Size 31.9 kB
Tags Source
SHA-256 checksum
How to use checksums
2d8865506cbfe196ea33fa81214e9c09f49c3583920ded5803368bf056565947
BLAKE2b-256 checksum
How to use checksums
834b399b61e7610c60a2567c9930c4593863ec967847696d5a08b8c2febea704
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.11.9

Release files / wowsql-3.9.0-py3-none-any.whl

Download URL wowsql-3.9.0-py3-none-any.whl
Size 31.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
a01c53db10e2d59edab533d40164cf75113702296eea3ca53b18bf6733be7efa
BLAKE2b-256 checksum
How to use checksums
fc5b552d24051532b8c2106bad6a501ddbb187117d8a3606192130ed4f42ac01
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.11.9

Release history Release notifications | RSS feed

3.10.0

2 release files

3.9.3

2 release files

3.9.2

2 release files

3.9.1

2 release files

This release

3.9.0 This release

2 release files

3.1.0

2 release files

3.0.1

2 release files

3.0.0

2 release files

2.1.0

2 release files

1.3.0

2 release files

1.2.0

2 release files

1.1.0

2 release files

1.0.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page