Official Go SDK for WOWSQL - MySQL Backend-as-a-Service with S3 Storage.
- ποΈ Full CRUD operations (Create, Read, Update, Delete)
- π Advanced filtering (eq, neq, gt, gte, lt, lte, like, isNull)
- π Pagination (limit, offset)
- π Sorting (orderBy)
- π― Fluent query builder API
- π Type-safe queries
- π Raw SQL queries
- π Table schema introspection
- π‘οΈ Comprehensive error handling
- π¦ S3-compatible storage for file management
- β¬οΈ File upload with automatic quota validation
- β¬οΈ File download (presigned URLs)
- π File listing with metadata
- ποΈ File deletion (single and batch)
- π Storage quota management
- π Multi-region S3 support
- π‘οΈ Client-side limit enforcement
## π Quick Start
### Database Operations
```go
package main
import (
"fmt"
"log"
"github.com/wowsql/wowsql-go/WOWSQL"
)
func main() {
// Initialize client
client := WOWSQL.NewClient(
"https://your-project.wowsql.com",
"your-api-key",
)
// Query data
users, err := client.Table("users").
Select("id", "name", "email").
Eq("status", "active").
Limit(10).
Execute()
if err != nil {
log.Fatal(err)
}
fmt.Printf("Found %d users\n", users.Count)
for _, user := range users.Data {
fmt.Printf("%s - %s\n", user["name"], user["email"])
}
}package main
import (
"fmt"
"log"
"os"
"github.com/wowsql/wowsql-go/WOWSQL"
)
func main() {
// Initialize storage client
storage := WOWSQL.NewStorageClient(
"https://your-project.wowsql.com",
"your-api-key",
)
// Upload file
fileData, _ := os.ReadFile("document.pdf")
result, err := storage.Upload(
fileData,
"uploads/document.pdf",
"application/pdf",
nil,
)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Uploaded: %s\n", result.URL)
// Check quota
quota, err := storage.GetQuota()
if err != nil {
log.Fatal(err)
}
fmt.Printf("Storage used: %.2fGB / %.2fGB\n",
quota.StorageUsedGB,
quota.StorageQuotaGB)
}package main
import (
"fmt"
"log"
"github.com/wowsql/wowsql-go/WOWSQL"
)
func main() {
auth := WOWSQL.NewAuthClient(WOWSQL.AuthConfig{
ProjectURL: "https://your-project.wowsql.com",
APIKey: "your-anon-key", // Use anon key for client-side, service key for server-side
})
// Sign up an end user
result, err := auth.SignUp("user@example.com", "SuperSecret123",
WOWSQL.WithFullName("End User"))
if err != nil {
log.Fatal(err)
}
fmt.Println("User ID:", result.User.ID)
fmt.Println("Access token:", result.Session.AccessToken)
// Fetch the same user via stored session token
user, err := auth.GetUser(result.Session.AccessToken)
if err != nil {
log.Fatal(err)
}
fmt.Println("Email verified:", user.EmailVerified)
// OAuth Authentication
oauthResp, err := auth.GetOAuthAuthorizationURL("github", "https://app.example.com/auth/callback")
if err != nil {
log.Fatal(err)
}
fmt.Println("Send user to:", oauthResp.AuthorizationURL)
// After callback, exchange code for tokens
redirectURI := "https://app.example.com/auth/callback"
oauthResult, err := auth.ExchangeOAuthCallback("github", "authorization_code", &redirectURI)
if err != nil {
log.Fatal(err)
}
fmt.Println("OAuth user:", oauthResult.User.Email)
// Password Reset
forgotResult, err := auth.ForgotPassword("user@example.com")
if err != nil {
log.Fatal(err)
}
fmt.Println(forgotResult["message"])
resetResult, err := auth.ResetPassword("reset_token", "newSecurePassword123")
if err != nil {
log.Fatal(err)
}
fmt.Println(resetResult["message"])
}client := WOWSQL.NewClient(
"https://your-project.wowsql.com",
"your-api-key",
)
// Select all columns
all, err := client.Table("users").Select("*").Execute()
// Select specific columns
users, err := client.Table("users").
Select("id", "name", "email").
Execute()
// With filters
active, err := client.Table("users").
Select("*").
Eq("status", "active").
Gt("age", 18).
Execute()
// With ordering
recent, err := client.Table("users").
Select("*").
OrderBy("created_at", WOWSQL.SortDesc).
Limit(10).
Execute()
// With pagination
page1, err := client.Table("users").
Select("*").
Limit(20).
Offset(0).
Execute()
// Pattern matching
gmailUsers, err := client.Table("users").
Select("*").
Like("email", "%@gmail.com").
Execute()
// Get first result
user, err := client.Table("users").
Eq("email", "john@example.com").
First()
if user != nil {
fmt.Printf("Found user: %s\n", user["name"])
}// Insert single record
result, err := client.Table("users").Insert(map[string]interface{}{
"name": "John Doe",
"email": "john@example.com",
"age": 30,
"status": "active",
})
if err != nil {
log.Fatal(err)
}
fmt.Printf("New user ID: %v\n", result.ID)// Update by ID
result, err := client.Table("users").UpdateByID(1, map[string]interface{}{
"name": "Jane Smith",
"age": 26,
})
fmt.Printf("Updated %d row(s)\n", result.AffectedRows)
// Update with conditions
updated, err := client.Table("users").
Where().
Eq("status", "inactive").
Update(map[string]interface{}{
"status": "active",
})// Delete by ID
result, err := client.Table("users").DeleteByID(1)
fmt.Printf("Deleted %d row(s)\n", result.AffectedRows)
// Delete with conditions
deleted, err := client.Table("users").
Where().
Eq("status", "deleted").
Delete()// 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
.IsNull("deleted_at")storage := WOWSQL.NewStorageClient(
"https://your-project.wowsql.com",
"your-api-key",
)
// Upload file from bytes
fileData, _ := os.ReadFile("document.pdf")
uploadResult, err := storage.Upload(
fileData,
"uploads/2024/document.pdf",
"application/pdf",
nil,
)
fmt.Printf("Uploaded: %s\n", uploadResult.URL)
// Check if file exists
exists, err := storage.FileExists("uploads/document.pdf")
if exists {
fmt.Println("File exists!")
}
// Get file information
info, err := storage.GetFileInfo("uploads/document.pdf")
fmt.Printf("Size: %d bytes\n", info.Size)
fmt.Printf("Modified: %s\n", info.LastModified)
// List files with prefix
files, err := storage.ListFiles("uploads/2024/", 0)
for _, file := range files {
fmt.Printf("%s: %d bytes\n", file.Key, file.Size)
}
// Download file (get presigned URL)
url, err := storage.Download("uploads/document.pdf", 3600)
fmt.Printf("Download URL: %s\n", url)
// URL is valid for 1 hour
// Delete single file
err = storage.DeleteFile("uploads/old-file.pdf")
// Delete multiple files
err = storage.DeleteFiles([]string{
"uploads/file1.pdf",
"uploads/file2.pdf",
"uploads/file3.pdf",
})
// Check quota
quota, err := storage.GetQuota()
fmt.Printf("Used: %.2f GB\n", quota.StorageUsedGB)
fmt.Printf("Available: %.2f GB\n", quota.StorageAvailableGB)
fmt.Printf("Usage: %.1f%%\n", quota.UsagePercentage)
// Check if enough storage before upload
if quota.StorageAvailableBytes < int64(len(fileData)) {
fmt.Println("Not enough storage!")
} else {
storage.Upload(fileData, "uploads/large-file.zip", "", nil)
}import (
"errors"
"github.com/wowsql/wowsql-go/WOWSQL"
)
users, err := client.Table("users").Select("*").Execute()
if err != nil {
var authErr *WOWSQL.AuthenticationError
var notFoundErr *WOWSQL.NotFoundError
var rateLimitErr *WOWSQL.RateLimitError
var networkErr *WOWSQL.NetworkError
switch {
case errors.As(err, &authErr):
fmt.Printf("Authentication error: %s\n", authErr.Message)
case errors.As(err, ¬FoundErr):
fmt.Printf("Not found: %s\n", notFoundErr.Message)
case errors.As(err, &rateLimitErr):
fmt.Printf("Rate limit exceeded: %s\n", rateLimitErr.Message)
case errors.As(err, &networkErr):
fmt.Printf("Network error: %s\n", err)
default:
fmt.Printf("Error: %s\n", err)
}
}
// Storage errors
_, err = storage.Upload(fileData, "uploads/file.pdf", "", nil)
if err != nil {
var limitErr *WOWSQL.StorageLimitExceededError
var storageErr *WOWSQL.StorageError
switch {
case errors.As(err, &limitErr):
fmt.Printf("Storage full: %s\n", limitErr.Message)
fmt.Printf("Required: %d bytes\n", limitErr.RequiredBytes)
fmt.Printf("Available: %d bytes\n", limitErr.AvailableBytes)
case errors.As(err, &storageErr):
fmt.Printf("Storage error: %s\n", storageErr.Message)
}
}// List all tables
tables, err := client.ListTables()
fmt.Printf("Tables: %v\n", tables)
// Get table schema
schema, err := client.GetTableSchema("users")
fmt.Printf("Columns: %d\n", len(schema.Columns))
for _, column := range schema.Columns {
fmt.Printf(" - %s (%s)\n", column.Name, column.Type)
}
// Raw SQL query
results, err := client.Query("SELECT COUNT(*) as count FROM users WHERE age > 18")
if len(results) > 0 {
fmt.Printf("Adult users: %v\n", results[0]["count"])
}
// Check API health
health, err := client.Health()
fmt.Printf("Status: %v\n", health["status"])import "time"
// Database client with custom timeout
client := WOWSQL.NewClientWithTimeout(
"https://your-project.wowsql.com",
"your-api-key",
60 * time.Second, // 60 seconds
)
// Storage client with custom timeout
storage := WOWSQL.NewStorageClientWithOptions(
"https://your-project.wowsql.com",
"your-api-key",
120 * time.Second, // 2 minutes for large files
true, // auto check quota
)// Disable automatic quota checking
storage := WOWSQL.NewStorageClientWithOptions(
"https://your-project.wowsql.com",
"your-api-key",
60 * time.Second,
false, // disable auto-check
)
// Manually check quota
quota, err := storage.GetQuota()
if quota.StorageAvailableBytes > int64(len(fileData)) {
checkQuota := false
storage.Upload(fileData, "uploads/file.pdf", "", &checkQuota)
}WOWSQL uses different API keys for different operations. Understanding which key to use is crucial for proper authentication.
β¨ One Project = One Set of Keys for ALL Operations
WOWSQL uses unified authentication - the same API keys work for both database operations AND authentication operations.
| Operation Type | Recommended Key | Alternative Key | Used By |
|---|---|---|---|
| Database Operations (CRUD) | Service Role Key (wowsql_service_...) |
Anonymous Key (wowsql_anon_...) |
WOWSQLClient |
| Authentication Operations (OAuth, sign-in) | Anonymous Key (wowsql_anon_...) |
Service Role Key (wowsql_service_...) |
ProjectAuthClient |
All keys are found in: WOWSQL Dashboard β Settings β API Keys or Authentication β PROJECT KEYS
-
Anonymous Key (
wowsql_anon_...) β¨ Unified Key- Location: "Anonymous Key (Public)"
- Used for:
- β Client-side auth operations (signup, login, OAuth)
- β Public/client-side database operations with limited permissions
- Safe to expose in frontend code (browser, mobile apps)
-
Service Role Key (
wowsql_service_...) β¨ Unified Key- Location: "Service Role Key (keep secret)"
- Used for:
- β Server-side auth operations (admin, full access)
- β Server-side database operations (full access, bypass RLS)
- NEVER expose in frontend code - server-side only!
Use Service Role Key or Anonymous Key for database operations:
package main
import "github.com/wowsql/wowsql-go/WOWSQL"
// Using Service Role Key (recommended for server-side, full access)
client := WOWSQL.NewClient(
"https://your-project.wowsql.com",
"wowsql_service_your-service-key-here", // Service Role Key
)
// Using Anonymous Key (for public/client-side access with limited permissions)
client := WOWSQL.NewClient(
"https://your-project.wowsql.com",
"wowsql_anon_your-anon-key-here", // Anonymous Key
)
// Query data
users, err := client.Table("users").Execute()β¨ UNIFIED AUTHENTICATION: Use the same keys as database operations!
package main
import "github.com/wowsql/wowsql-go/WOWSQL"
// Using Anonymous Key (recommended for client-side auth operations)
auth := WOWSQL.NewAuthClient(WOWSQL.AuthConfig{
ProjectURL: "https://your-project.wowsql.com",
APIKey: "wowsql_anon_your-anon-key-here", // Same key as database operations!
})
// Using Service Role Key (for server-side auth operations)
auth := WOWSQL.NewAuthClient(WOWSQL.AuthConfig{
ProjectURL: "https://your-project.wowsql.com",
APIKey: "wowsql_service_your-service-key-here", // Same key as database operations!
})
// OAuth authentication
oauthResp, err := auth.GetOAuthAuthorizationURL("github", "https://app.example.com/auth/callback")Note: The PublicAPIKey parameter is deprecated but still works for backward compatibility. Use APIKey instead.
Best practice: Use environment variables for API keys:
package main
import (
"os"
"github.com/wowsql/wowsql-go/WOWSQL"
)
// UNIFIED AUTHENTICATION: Same keys for both operations!
// Database operations - Service Role Key
dbClient := WOWSQL.NewClient(
os.Getenv("WOWSQL_PROJECT_URL"),
os.Getenv("WOWSQL_SERVICE_ROLE_KEY"), // or WOWSQL_ANON_KEY
)
// Authentication operations - Use the SAME key!
authClient := WOWSQL.NewAuthClient(WOWSQL.AuthConfig{
ProjectURL: os.Getenv("WOWSQL_PROJECT_URL"),
APIKey: os.Getenv("WOWSQL_ANON_KEY"), // Same key for client-side auth
// Or use WOWSQL_SERVICE_ROLE_KEY for server-side auth
})β¨ UNIFIED AUTHENTICATION:
WOWSQLClientβ Uses Service Role Key or Anonymous Key for database operationsProjectAuthClientβ Uses Anonymous Key (client-side) or Service Role Key (server-side) for authentication operations- Same keys work for both database AND authentication operations! π
- Anonymous Key (
wowsql_anon_...) β Client-side operations (auth + database) - Service Role Key (
wowsql_service_...) β Server-side operations (auth + database)
- Never expose Service Role Key in client-side code or public repositories
- Use Anonymous Key for client-side authentication flows (same key as database operations)
- Use Anonymous Key for public database access with limited permissions
- Store keys in environment variables, never hardcode them
- Rotate keys regularly if compromised
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.
package main
import "github.com/wowsql/wowsql-go/WOWSQL"
func main() {
// Initialize schema client with SERVICE ROLE KEY
schema := WOWSQL.NewSchemaClient(
"https://your-project.wowsql.com",
"service_xyz789...", // β οΈ Backend only! Never expose!
)
}// Create a new table
trueVal := true
err := schema.CreateTable(WOWSQL.CreateTableRequest{
TableName: "products",
Columns: []WOWSQL.ColumnDefinition{
{Name: "id", Type: "INT", AutoIncrement: &trueVal},
{Name: "name", Type: "VARCHAR(255)", NotNull: &trueVal},
{Name: "price", Type: "DECIMAL(10,2)", NotNull: &trueVal},
{Name: "category", Type: "VARCHAR(100)"},
{Name: "created_at", Type: "TIMESTAMP", Default: WOWSQL.strPtr("CURRENT_TIMESTAMP")},
},
PrimaryKey: WOWSQL.strPtr("id"),
Indexes: []WOWSQL.IndexDefinition{
{Name: "idx_category", Columns: []string{"category"}},
{Name: "idx_price", Columns: []string{"price"}},
},
})
if err != nil {
log.Fatal(err)
}
fmt.Println("Table created successfully!")// Add a new column
err := schema.AlterTable(WOWSQL.AlterTableRequest{
TableName: "products",
AddColumns: []WOWSQL.ColumnDefinition{
{Name: "stock_quantity", Type: "INT", Default: WOWSQL.strPtr("0")},
},
})
// Modify an existing column
err = schema.AlterTable(WOWSQL.AlterTableRequest{
TableName: "products",
ModifyColumns: []WOWSQL.ColumnDefinition{
{Name: "price", Type: "DECIMAL(12,2)"}, // Increase precision
},
})
// Drop a column
err = schema.AlterTable(WOWSQL.AlterTableRequest{
TableName: "products",
DropColumns: []string{"category"},
})
// Rename a column
err = schema.AlterTable(WOWSQL.AlterTableRequest{
TableName: "products",
RenameColumns: []WOWSQL.RenameColumn{
{OldName: "name", NewName: "product_name"},
},
})// Drop a table
err := schema.DropTable("old_table", false)
// Drop with CASCADE (removes dependent objects)
err = schema.DropTable("products", true)// Execute custom schema SQL
err := schema.ExecuteSQL(`
CREATE INDEX idx_product_name
ON products(product_name);
`)
// Add a foreign key constraint
err = schema.ExecuteSQL(`
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 client code
- Never commit service keys to version control
- Don't use anonymous keys for schema operations (will fail)
package main
import (
"fmt"
"log"
"os"
"github.com/wowsql/wowsql-go/WOWSQL"
)
func runMigration() error {
schema := WOWSQL.NewSchemaClient(
os.Getenv("WOWSQL_PROJECT_URL"),
os.Getenv("WOWSQL_SERVICE_KEY"), // From env var
)
// Create users table
trueVal := true
err := schema.CreateTable(WOWSQL.CreateTableRequest{
TableName: "users",
Columns: []WOWSQL.ColumnDefinition{
{Name: "id", Type: "INT", AutoIncrement: &trueVal},
{Name: "email", Type: "VARCHAR(255)", Unique: &trueVal, NotNull: &trueVal},
{Name: "name", Type: "VARCHAR(255)", NotNull: &trueVal},
{Name: "created_at", Type: "TIMESTAMP", Default: WOWSQL.strPtr("CURRENT_TIMESTAMP")},
},
PrimaryKey: WOWSQL.strPtr("id"),
Indexes: []WOWSQL.IndexDefinition{
{Name: "idx_email", Columns: []string{"email"}},
},
})
if err != nil {
return fmt.Errorf("migration failed: %w", err)
}
fmt.Println("Migration completed!")
return nil
}
func main() {
if err := runMigration(); err != nil {
log.Fatal(err)
}
}import (
"errors"
"github.com/wowsql/wowsql-go/WOWSQL"
)
schema := WOWSQL.NewSchemaClient(
"https://your-project.wowsql.com",
"service_xyz...",
)
err := schema.CreateTable(WOWSQL.CreateTableRequest{
TableName: "test",
Columns: []WOWSQL.ColumnDefinition{
{Name: "id", Type: "INT"},
},
})
if err != nil {
var permErr *WOWSQL.PermissionError
if errors.As(err, &permErr) {
fmt.Printf("Permission denied: %s\n", permErr.Message)
fmt.Println("Make sure you're using a SERVICE ROLE KEY, not an anonymous key!")
} else {
fmt.Printf("Error: %s\n", err)
}
}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 Anonymous Key (client-side) or Service Role Key (server-side)
- Verify the key is copied correctly (no extra spaces)
Error: "Authentication failed"
- Check that you're using the correct key: Anonymous Key for client-side, Service Role Key for server-side
- Verify the project URL matches your dashboard
- Ensure the key hasn't been revoked or expired
- Go:
1.21+
- π Documentation
- π Website
- π¬ Discord
- π Issues
MIT License - see LICENSE file for details.
Contributions are welcome! Please open an issue or submit a pull request.
- Email: support@wowsql.com
- Discord: https://discord.gg/WOWSQL
- Documentation: https://wowsql.com/docs
Made with β€οΈ by the WOWSQL Team