Official Python client for WowSQL - MySQL Backend-as-a-Service with S3 Storage.
pip install wowsqlfrom wowsql import WowSQLClient
# Initialize client
client = WowSQLClient(
project_url="https://your-project.wowsql.com",
api_key="your-api-key" # Get from dashboard
)
# Select data
response = client.table("users").select("*").limit(10).execute()
print(response.data) # [{'id': 1, 'name': 'John', ...}, ...]
# Insert data
result = client.table("users").insert({
"name": "Jane Doe",
"email": "jane@example.com",
"age": 25
}).execute()
# Update data
result = client.table("users").update({
"name": "Jane Smith"
}).eq("id", 1).execute()
# Delete data
result = client.table("users").delete().eq("id", 1).execute()from wowsql import WowSQLStorage
# Initialize storage client
storage = WowSQLStorage(
project_url="https://your-project.wowsql.com",
api_key="your-api-key"
)
# Upload file
storage.upload("local-file.pdf", "uploads/document.pdf")
# Download file (get presigned URL)
url = storage.download("uploads/document.pdf")
print(url)
# List files
files = storage.list_files(prefix="uploads/")
for file in files:
print(f"{file.key}: {file.size} bytes")
# Delete file
storage.delete_file("uploads/document.pdf")
# Check storage quota
quota = storage.get_quota()
print(f"Used: {quota.used_bytes}/{quota.limit_bytes} bytes")
print(f"Available: {quota.available_bytes} bytes")from wowsql import ProjectAuthClient
auth = ProjectAuthClient(
project_url="https://your-project.wowsql.com",
public_api_key="your-public-auth-key"
)result = auth.sign_up(
email="user@example.com",
password="SuperSecret123",
full_name="End User",
user_metadata={"referrer": "landing"}
)
print(result.user.email, result.session.access_token)session = auth.sign_in(
email="user@example.com",
password="SuperSecret123"
).session
auth.set_session(
access_token=session.access_token,
refresh_token=session.refresh_token
)
current_user = auth.get_user()
print(current_user.id, current_user.email_verified)# Step 1: Get authorization URL
oauth = auth.get_oauth_authorization_url(
provider="github",
redirect_uri="https://app.example.com/auth/callback"
)
print("Send the user to:", oauth["authorization_url"])
# Step 2: After user authorizes, exchange code for tokens
# (In your callback handler)
result = auth.exchange_oauth_callback(
provider="github",
code="authorization_code_from_callback",
redirect_uri="https://app.example.com/auth/callback"
)
print(f"Logged in as: {result.user.email}")
print(f"Access token: {result.session.access_token}")# Request password reset
result = auth.forgot_password(email="user@example.com")
print(result["message"]) # "If that email exists, a password reset link has been sent"
# Reset password (after user clicks email link)
result = auth.reset_password(
- β
Full CRUD operations (Create, Read, Update, Delete)
- β
Advanced filtering (eq, neq, gt, gte, lt, lte, like, is_null)
- β
Pagination (limit, offset)
- β
Sorting (order_by)
- β
Raw SQL queries
- β
Table schema introspection
- β
Automatic rate limit handling
- β
Built-in error handling
- β
Context manager support
### Storage Features (NEW!)
- β
S3-compatible storage client
- β
File upload with automatic quota validation
- β
File download (presigned URLs)
- β
File listing with metadata
- β
File deletion (single and batch)
- β
Storage quota management
- β
Multi-region support
- β
Client-side limit enforcement
## Usage Examples
### Select Queries
```python
from wowsql import WowSQLClient
client = WowSQLClient(
project_url="https://your-project.wowsql.com",
api_key="your-api-key"
)
# Select all columns
users = client.table("users").select("*").execute()
# Select specific columns
users = client.table("users").select("id", "name", "email").execute()
# With filters
active_users = client.table("users") \
.select("*") \
.eq("status", "active") \
.gt("age", 18) \
.execute()
# With ordering
recent_users = client.table("users") \
.select("*") \
.order_by("created_at", desc=True) \
.limit(10) \
.execute()
# With pagination
page_1 = client.table("users").select("*").limit(20).offset(0).execute()
page_2 = client.table("users").select("*").limit(20).offset(20).execute()
# Pattern matching
gmail_users = client.table("users") \
.select("*") \
.like("email", "%@gmail.com") \
.execute()# Insert single row
result = client.table("users").insert({
"name": "John Doe",
"email": "john@example.com",
"age": 30
}).execute()
# Insert multiple rows
result = client.table("users").insert([
{"name": "Alice", "email": "alice@example.com"},
{"name": "Bob", "email": "bob@example.com"}
]).execute()# Update by ID
result = client.table("users").update({
"name": "Jane Smith",
"age": 26
}).eq("id", 1).execute()
# Update with conditions
result = client.table("users").update({
"status": "inactive"
}).lt("last_login", "2024-01-01").execute()# Delete by ID
result = client.table("users").delete().eq("id", 1).execute()
# Delete with conditions
result = client.table("users").delete() \
.eq("status", "deleted") \
.lt("created_at", "2023-01-01") \
.execute()# Equal
.eq("status", "active")
# Not equal
.neq("status", "deleted")
# Greater than
.gt("age", 18)
# Greater than or equal
.gte("age", 18)
# Less than
.lt("age", 65)
# Less than or equal
.lte("age", 65)
# Pattern matching (SQL LIKE)
.like("email", "%@gmail.com")
# Is null
.is_null("deleted_at")from wowsql import WowSQLStorage, StorageLimitExceededError
storage = WowSQLStorage(
project_url="https://your-project.wowsql.com",
api_key="your-api-key"
)
# Upload file with metadata
storage.upload(
"document.pdf",
"uploads/2024/document.pdf",
metadata={"category": "reports"}
)
# Upload file object
with open("image.jpg", "rb") as f:
storage.upload_fileobj(f, "images/photo.jpg")
# Check if file exists
if storage.file_exists("uploads/document.pdf"):
print("File exists!")
# Get file information
info = storage.get_file_info("uploads/document.pdf")
print(f"Size: {info.size} bytes")
print(f"Modified: {info.last_modified}")
# List files with prefix
files = storage.list_files(prefix="uploads/2024/", limit=100)
for file in files:
print(f"{file.key}: {file.size} bytes")
# Download file to local path
storage.download("uploads/document.pdf", "local-copy.pdf")
# Get presigned URL (valid for 1 hour)
url = storage.download("uploads/document.pdf")
print(url) # https://s3.amazonaws.com/...
# Delete single file
storage.delete_file("uploads/old-file.pdf")
# Delete multiple files
storage.delete_files([
"uploads/file1.pdf",
"uploads/file2.pdf",
"uploads/file3.pdf"
])
# Check quota before upload
quota = storage.get_quota()
file_size = storage.get_file_size("large-file.zip")
if quota.available_bytes < file_size:
print(f"Not enough storage! Need {file_size} bytes, have {quota.available_bytes}")
else:
storage.upload("large-file.zip", "backups/large-file.zip")
# Handle storage limit errors
try:
storage.upload("huge-file.zip", "uploads/huge-file.zip")
except StorageLimitExceededError as e:
print(f"Storage limit exceeded: {e}")
print("Please upgrade your plan or delete old files")# Automatically closes connection
with WowSQLClient(project_url="...", api_key="...") as client:
users = client.table("users").select("*").execute()
print(users.data)
# Connection closed here
# Works with storage too
with WowSQLStorage(project_url="...", api_key="...") as storage:
files = storage.list_files()
print(f"Total files: {len(files)}")from wowsql import (
WowSQLClient,
WowSQLError,
StorageError,
StorageLimitExceededError
)
client = WowSQLClient(project_url="...", api_key="...")
try:
users = client.table("users").select("*").execute()
except WowSQLError as e:
print(f"Database error: {e}")
storage = WowSQLStorage(project_url="...", api_key="...")
try:
storage.upload("file.pdf", "uploads/file.pdf")
except StorageLimitExceededError as e:
print(f"Storage full: {e}")
except StorageError as e:
print(f"Storage error: {e}")# Check API health
health = client.health()
print(health) # {'status': 'healthy', ...}
# List all tables
tables = client.list_tables()
print(tables) # ['users', 'posts', 'comments']
# Get table schema
schema = client.describe_table("users")
print(schema) # {'columns': [...], 'row_count': 100}All database queries return a response object:
response = client.table("users").select("*").limit(10).execute()
# Access data
print(response.data) # [{'id': 1, ...}, {'id': 2, ...}]
# Access count
print(response.count) # 10
# Check for errors
if response.error:
print(response.error)# Custom timeout (default: 30 seconds)
client = WowSQLClient(
project_url="...",
api_key="...",
timeout=60 # 60 seconds
)
# Storage timeout (default: 60 seconds for large files)
storage = WowSQLStorage(
project_url="...",
api_key="...",
timeout=120 # 2 minutes
)# Disable automatic quota checking before uploads
storage = WowSQLStorage(
project_url="...",
api_key="...",
auto_check_quota=False
)
# Manually check quota
quota = storage.get_quota()
if quota.available_bytes > file_size:
storage.upload("file.pdf", "uploads/file.pdf", check_quota=False)WOWSQL uses different API keys for different operations. Understanding which key to use is crucial for proper authentication.
| Operation Type | Recommended Key | Alternative Key | Used By |
|---|---|---|---|
| Database Operations (CRUD) | Service Role Key (wowbase_service_...) |
Anonymous Key (wowbase_anon_...) |
WowSQLClient |
| Authentication Operations (OAuth, sign-in) | Public API Key (wowbase_auth_...) |
Service Role Key (wowbase_service_...) |
ProjectAuthClient |
All keys are found in: WOWSQL Dashboard β Authentication β PROJECT KEYS
-
Service Role Key (
wowbase_service_...)- Location: "Service Role Key (keep secret)"
- Used for: Database CRUD operations (recommended for server-side)
- Can also be used for authentication operations (fallback)
- Important: Click the eye icon to reveal this key
-
Public API Key (
wowbase_auth_...)- Location: "Public API Key"
- Used for: OAuth, sign-in, sign-up, user management
- Recommended for client-side/public authentication flows
-
Anonymous Key (
wowbase_anon_...)- Location: "Anonymous Key"
- Used for: Public/client-side database operations with limited permissions
- Optional: Use when exposing database access to frontend/client
Use Service Role Key or Anonymous Key for database operations:
from wowsql import WowSQLClient
# Using Service Role Key (recommended for server-side, full access)
client = WowSQLClient(
project_url="https://your-project.wowsql.com",
api_key="wowbase_service_your-service-key-here" # Service Role Key
)
# Using Anonymous Key (for public/client-side access with limited permissions)
client = WowSQLClient(
project_url="https://your-project.wowsql.com",
api_key="wowbase_anon_your-anon-key-here" # Anonymous Key
)
# Query data
users = client.table("users").get()Use Public API Key or Service Role Key for authentication:
from wowsql import ProjectAuthClient
# Using Public API Key (recommended for OAuth, sign-in, sign-up)
auth = ProjectAuthClient(
project_url="https://your-project.wowsql.com",
public_api_key="wowbase_auth_your-public-key-here" # Public API Key
)
# Using Service Role Key (can be used for auth operations too)
auth = ProjectAuthClient(
project_url="https://your-project.wowsql.com",
public_api_key="wowbase_service_your-service-key-here" # Service Role Key
)
# OAuth authentication
oauth_url = auth.get_oauth_authorization_url(
provider="github",
redirect_uri="https://app.example.com/auth/callback"
)Best practice: Use environment variables for API keys:
import os
from wowsql import WowSQLClient, ProjectAuthClient
# Database operations - Service Role Key
db_client = WowSQLClient(
project_url=os.getenv("WOWSQL_PROJECT_URL"),
api_key=os.getenv("WOWSQL_SERVICE_ROLE_KEY") # or WOWSQL_ANON_KEY
)
# Authentication operations - Public API Key
auth_client = ProjectAuthClient(
project_url=os.getenv("WOWSQL_PROJECT_URL"),
public_api_key=os.getenv("WOWSQL_PUBLIC_API_KEY")
)WowSQLClientβ Uses Service Role Key or Anonymous Key for database operationsProjectAuthClientβ Uses Public API Key or Service Role Key for authentication operations- Service Role Key can be used for both database AND authentication operations
- Public API Key is specifically for authentication operations only
- Anonymous Key is optional and provides limited permissions for public database access
- Never expose Service Role Key in client-side code or public repositories
- Use Public API Key for client-side authentication flows
- Use Anonymous Key for public database access with limited permissions
- Store keys in environment variables, never hardcode them
- Rotate keys regularly if compromised
Error: "Invalid API key for project"
- Ensure you're using the correct key type for the operation
- Database operations require Service Role Key or Anonymous Key
- Authentication operations require Public API Key or Service Role Key
- Verify the key is copied correctly (no extra spaces)
Error: "Authentication failed"
- Check that you're using Public API Key (not Anonymous Key) for auth operations
- Verify the project URL matches your dashboard
- Ensure the key hasn't been revoked or expired
from wowsql import WowSQLClient
client = WowSQLClient(project_url="...", api_key="...")
# Create a new post
post = client.table("posts").insert({
"title": "Hello World",
"content": "My first blog post",
"author_id": 1,
"published": True
}).execute()
# Get published posts
posts = client.table("posts") \
.select("id", "title", "content", "created_at") \
.eq("published", True) \
.order_by("created_at", desc=True) \
.limit(10) \
.execute()
# Get post with comments
post = client.table("posts").select("*").eq("id", 1).execute()
comments = client.table("comments").select("*").eq("post_id", 1).execute()from wowsql import WowSQLClient, WowSQLStorage
client = WowSQLClient(project_url="...", api_key="...")
storage = WowSQLStorage(project_url="...", api_key="...")
# Upload user avatar
user_id = 123
avatar_path = f"avatars/{user_id}.jpg"
storage.upload("avatar.jpg", avatar_path)
# Save avatar URL in database
avatar_url = storage.download(avatar_path)
client.table("users").update({
"avatar_url": avatar_url
}).eq("id", user_id).execute()
# List user's files
user_files = storage.list_files(prefix=f"users/{user_id}/")
print(f"User has {len(user_files)} files")- Python 3.8+
- requests>=2.31.0
# Clone repository
git clone https://github.com/wowsql/wowsql.git
cd wowsql/sdk/python
# Install in development mode
pip install -e ".[dev]"
# Run tests
pytest
# Run examples
python examples/basic_usage.py
python examples/storage_usage.pyContributions are welcome! Please open an issue or submit a pull request.
MIT License - see LICENSE file for details.
- π Documentation
- π Website
- π¬ Discord
- π Issues
Programmatically manage your database schema with the WowSQLSchema client.
β οΈ IMPORTANT: Schema operations require a Service Role Key (service_*). Anonymous keys will return a 403 Forbidden error.
from wowsql import WowSQLSchema
# Initialize schema client with SERVICE ROLE KEY
schema = WowSQLSchema(
project_url="https://your-project.wowsql.com",
service_key="service_xyz789..." # β οΈ Backend only! Never expose!
)# Create a new table
schema.create_table(
table_name="products",
columns=[
{"name": "id", "type": "INT", "auto_increment": True},
{"name": "name", "type": "VARCHAR(255)", "not_null": True},
{"name": "price", "type": "DECIMAL(10,2)", "not_null": True},
{"name": "category", "type": "VARCHAR(100)"},
{"name": "created_at", "type": "TIMESTAMP", "default": "CURRENT_TIMESTAMP"}
],
primary_key="id",
indexes=[
{"name": "idx_category", "columns": ["category"]},
{"name": "idx_price", "columns": ["price"]}
]
)
print("Table 'products' created successfully!")# Add a new column
schema.alter_table(
table_name="products",
add_columns=[
{"name": "stock_quantity", "type": "INT", "default": "0"}
]
)
# Modify an existing column
schema.alter_table(
table_name="products",
modify_columns=[
{"name": "price", "type": "DECIMAL(12,2)"} # Increase precision
]
)
# Drop a column
schema.alter_table(
table_name="products",
drop_columns=["category"]
)
# Rename a column
schema.alter_table(
table_name="products",
rename_columns=[
{"old_name": "name", "new_name": "product_name"}
]
)# Drop a table
schema.drop_table("old_table")
# Drop with CASCADE (removes dependent objects)
schema.drop_table("products", cascade=True)# Execute custom schema SQL
schema.execute_sql("""
CREATE INDEX idx_product_name
ON products(product_name);
""")
# Add a foreign key constraint
schema.execute_sql("""
ALTER TABLE orders
ADD CONSTRAINT fk_product
FOREIGN KEY (product_id)
REFERENCES products(id);
""")- Use service role keys only in backend/server code
- Store service keys in environment variables
- Use anonymous keys for client-side data operations
- Test schema changes in development first
- Never expose service role keys in frontend code
- Never commit service keys to version control
- Don't use anonymous keys for schema operations (will fail)
import os
from wowsql import WowSQLSchema
def run_migration():
schema = WowSQLSchema(
project_url=os.getenv("WOWSQL_PROJECT_URL"),
service_key=os.getenv("WOWSQL_SERVICE_KEY") # From env var
)
# Create users table
schema.create_table(
table_name="users",
columns=[
{"name": "id", "type": "INT", "auto_increment": True},
{"name": "email", "type": "VARCHAR(255)", "unique": True, "not_null": True},
{"name": "name", "type": "VARCHAR(255)", "not_null": True},
{"name": "created_at", "type": "TIMESTAMP", "default": "CURRENT_TIMESTAMP"}
],
primary_key="id",
indexes=[{"name": "idx_email", "columns": ["email"]}]
)
print("Migration completed!")
if __name__ == "__main__":
run_migration()from wowsql import WowSQLSchema, PermissionError
try:
schema = WowSQLSchema(
project_url="https://your-project.wowsql.com",
service_key="service_xyz..."
)
schema.create_table("test", [{"name": "id", "type": "INT"}])
except PermissionError as e:
print(f"Permission denied: {e}")
print("Make sure you're using a SERVICE ROLE KEY, not an anonymous key!")
except Exception as e:
print(f"Error: {e}")- Email: support@wowsql.com
- Discord: https://discord.gg/wowsql
- Documentation: https://wowsql.com/docs
Made with β€οΈ by the WowSQL Team