Official TypeScript/JavaScript SDK for WOWSQL - The powerful MySQL Backend-as-a-Service platform.
- π Zero Configuration - Get started in seconds
- π Type-Safe - Full TypeScript support with generics
- π― Fluent API - Intuitive query builder pattern
- β‘ Lightweight - Minimal dependencies (only axios)
- π‘οΈ Error Handling - Comprehensive error messages
- π¦ Tree-Shakeable - Import only what you need
# npm
npm install @wowsql/sdk
# yarn
yarn add @wowsql/sdk
# pnpm
pnpm add @wowsql/sdkimport WOWSQLClient from '@wowsql/sdk';
// Initialize client
const client = new WOWSQLClient({
projectUrl: 'myproject', // Your project subdomain
apiKey: 'your-api-key-here'
});
// Query data
const users = await client.table('users')
.select(['id', 'name', 'email'])
.filter({ column: 'age', operator: 'gt', value: 18 })
.order('created_at', 'desc')
.limit(10)
.get();
console.log(users.data); // Array of user recordsThe SDK ships with a dedicated ProjectAuthClient to integrate with the project-level auth service (signup, login, sessions, OAuth helpers). Provide the project slug (or full URL) and the public auth key exposed in the dashboard.
import { ProjectAuthClient } from '@wowsql/sdk';
const auth = new ProjectAuthClient({
projectUrl: 'myproject', // or https://myproject.wowsql.com
publicApiKey: 'public-auth-key'
});const { user, session } = await auth.signUp({
email: 'user@example.com',
password: 'SuperSecret123',
full_name: 'Demo User',
user_metadata: { referrer: 'landing-page' }
});
console.log('New auth user id:', user?.id);
console.log('Access token:', session.accessToken);const { session } = await auth.signIn({
email: 'user@example.com',
password: 'SuperSecret123'
});
// Save the session and reuse on page refresh
auth.setSession({
accessToken: session.accessToken,
refreshToken: session.refreshToken
});
const currentUser = await auth.getUser(); // reads the stored token
console.log('Welcome back,', currentUser.full_name);Complete OAuth flow with callback handling:
// Step 1: Get authorization URL
const { authorizationUrl } = await auth.getOAuthAuthorizationUrl(
'github',
'https://app.your-domain.com/auth/callback'
);
window.location.href = authorizationUrl;
// Step 2: After user authorizes, exchange code for tokens
// (In your callback handler)
const result = await auth.exchangeOAuthCallback(
'github',
code, // from URL query params
'https://app.your-domain.com/auth/callback'
);
console.log('Logged in as:', result.user?.email);
console.log('Access token:', result.session.accessToken);// Request password reset
const forgotResult = await auth.forgotPassword('user@example.com');
console.log(forgotResult.message); // "If that email exists, a password reset link has been sent"
// Reset password (after user clicks email link)
const resetResult = await auth.resetPassword(
token, // from email link
'newSecurePassword123'
);
console.log(resetResult.message); // "Password reset successfully! You can now login with your new password"The client exposes getSession, setSession, and clearSession utilities so you can wire tokens into your own persistence layer (localStorage, cookies, etc.):
// Get current session
const session = auth.getSession();
console.log('Access token:', session.accessToken);
// Set session (e.g., from localStorage on page load)
auth.setSession({
accessToken: localStorage.getItem('access_token')!,
refreshToken: localStorage.getItem('refresh_token')!
});
// Clear session (logout)
auth.clearSession();const client = new WOWSQLClient({
projectUrl: 'myproject', // Your project subdomain
apiKey: 'your-api-key' // Your API key from dashboard
});const client = new WOWSQLClient({
projectUrl: 'myproject',
apiKey: 'your-api-key',
baseDomain: 'wowsql.com', // Custom domain (optional)
secure: true, // Use HTTPS (default: true)
timeout: 30000 // Request timeout in ms (default: 30000)
});const client = new WOWSQLClient({
projectUrl: 'https://myproject.wowsql.com',
apiKey: 'your-api-key'
});interface User {
id: number;
name: string;
email: string;
age: number;
created_at: string;
}
const client = new WOWSQLClient({
projectUrl: 'myproject',
apiKey: 'your-api-key'
});
// Type-safe queries
const users = await client.table<User>('users').get();
users.data.forEach(user => {
console.log(user.name); // Type-safe!
});// Create a single user
const result = await client.table('users').create({
name: 'John Doe',
email: 'john@example.com',
age: 25
});
console.log(result.id); // New record ID// Get all records
const allUsers = await client.table('users').get();
// Get by ID
const user = await client.table('users').getById(1);
// Select specific columns
const users = await client.table('users')
.select(['id', 'name', 'email'])
.get();
// With filters
const adults = await client.table('users')
.filter({ column: 'age', operator: 'gte', value: 18 })
.get();
// Multiple filters
const result = await client.table('users')
.filter({ column: 'age', operator: 'gte', value: 18 })
.filter({ column: 'country', operator: 'eq', value: 'USA' })
.get();
// With sorting
const sorted = await client.table('users')
.order('created_at', 'desc')
.get();
// With pagination
const page1 = await client.table('users')
.limit(10)
.offset(0)
.get();
// Get first record
const firstUser = await client.table('users')
.filter({ column: 'email', operator: 'eq', value: 'john@example.com' })
.first();// Update by ID
const result = await client.table('users').update(1, {
name: 'Jane Doe',
age: 26
});
console.log(result.affected_rows); // Number of rows updated// Delete by ID
const result = await client.table('users').delete(1);
console.log(result.affected_rows); // Number of rows deleted// Equal
.filter({ column: 'status', operator: 'eq', value: 'active' })
// Not equal
.filter({ column: 'status', operator: 'neq', value: 'deleted' })
// Greater than
.filter({ column: 'age', operator: 'gt', value: 18 })
// Greater than or equal
.filter({ column: 'age', operator: 'gte', value: 18 })
// Less than
.filter({ column: 'price', operator: 'lt', value: 100 })
// Less than or equal
.filter({ column: 'price', operator: 'lte', value: 100 })
// Like (pattern matching)
.filter({ column: 'name', operator: 'like', value: '%John%' })
// Is null
.filter({ column: 'deleted_at', operator: 'is', value: null })// Combine multiple operations
const result = await client.table<Product>('products')
.select(['id', 'name', 'price', 'category'])
.filter({ column: 'category', operator: 'eq', value: 'Electronics' })
.filter({ column: 'price', operator: 'lt', value: 1000 })
.filter({ column: 'in_stock', operator: 'eq', value: true })
.order('price', 'asc')
.limit(20)
.offset(0)
.get();
console.log(`Found ${result.total} products`);
console.log(`Showing ${result.count} products`);
result.data.forEach(product => {
console.log(`${product.name}: $${product.price}`);
});// Execute custom SQL (read-only)
const results = await client.query<User>(`
SELECT id, name, email
FROM users
WHERE age > 18
ORDER BY created_at DESC
LIMIT 10
`);// List all tables
const tables = await client.listTables();
console.log(tables); // ['users', 'posts', 'comments']
// Get table schema
const schema = await client.getTableSchema('users');
console.log(schema.columns);
console.log(schema.primary_key);// Check API health
const health = await client.health();
console.log(health.status); // 'ok'import { WOWSQLClient, WOWSQLError } from '@wowsql/sdk';
try {
const user = await client.table('users').getById(999);
} catch (error) {
if (error instanceof WOWSQLError) {
console.error(`Error ${error.statusCode}: ${error.message}`);
console.error(error.response); // Full error response
} else {
console.error('Unexpected error:', error);
}
}Main client class for interacting with WOWSQL API.
table<T>(tableName: string): Table<T>- Get table interfacelistTables(): Promise<string[]>- List all tablesgetTableSchema(tableName: string): Promise<TableSchema>- Get table schemaquery<T>(sql: string): Promise<T[]>- Execute raw SQLhealth(): Promise<{status: string, timestamp: string}>- Health check
Fluent interface for table operations.
select(columns: string | string[]): QueryBuilder<T>- Select columnsfilter(filter: FilterExpression): QueryBuilder<T>- Add filterget(options?: QueryOptions): Promise<QueryResponse<T>>- Get recordsgetById(id: string | number): Promise<T>- Get by IDcreate(data: Partial<T>): Promise<CreateResponse>- Create recordupdate(id: string | number, data: Partial<T>): Promise<UpdateResponse>- Update recorddelete(id: string | number): Promise<DeleteResponse>- Delete record
Chainable query builder.
select(columns: string | string[]): this- Select columnsfilter(filter: FilterExpression): this- Add filterorder(column: string, direction?: 'asc' | 'desc'): this- Order bylimit(limit: number): this- Limit resultsoffset(offset: number): this- Skip recordsget(options?: QueryOptions): Promise<QueryResponse<T>>- Execute queryfirst(): Promise<T | null>- Get first record
// app/api/users/route.ts
import { NextResponse } from 'next/server';
import WOWSQLClient from '@wowsql/sdk';
const client = new WOWSQLClient({
projectUrl: process.env.WOWSQL_PROJECT!,
apiKey: process.env.WOWSQL_API_KEY!
});
export async function GET(request: Request) {
try {
const { searchParams } = new URL(request.url);
const page = parseInt(searchParams.get('page') || '1');
const limit = 20;
const users = await client.table('users')
.select(['id', 'name', 'email', 'created_at'])
.order('created_at', 'desc')
.limit(limit)
.offset((page - 1) * limit)
.get();
return NextResponse.json(users);
} catch (error) {
return NextResponse.json(
{ error: 'Failed to fetch users' },
{ status: 500 }
);
}
}
export async function POST(request: Request) {
try {
const body = await request.json();
const result = await client.table('users').create(body);
return NextResponse.json(result, { status: 201 });
} catch (error) {
return NextResponse.json(
{ error: 'Failed to create user' },
{ status: 500 }
);
}
}// hooks/useWOWSQL.ts
import { useState, useEffect } from 'react';
import WOWSQLClient from '@wowsql/sdk';
const client = new WOWSQLClient({
projectUrl: process.env.NEXT_PUBLIC_WOWSQL_PROJECT!,
apiKey: process.env.NEXT_PUBLIC_WOWSQL_API_KEY!
});
export function useUsers() {
const [users, setUsers] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
async function fetchUsers() {
try {
const result = await client.table('users').get();
setUsers(result.data);
} catch (err) {
setError(err);
} finally {
setLoading(false);
}
}
fetchUsers();
}, []);
return { users, loading, error };
}// server.ts
import express from 'express';
import WOWSQLClient from '@wowsql/sdk';
const app = express();
const client = new WOWSQLClient({
projectUrl: process.env.WOWSQL_PROJECT!,
apiKey: process.env.WOWSQL_API_KEY!
});
app.use(express.json());
// Get all posts
app.get('/api/posts', async (req, res) => {
try {
const { page = 1, limit = 10 } = req.query;
const posts = await client.table('posts')
.order('created_at', 'desc')
.limit(Number(limit))
.offset((Number(page) - 1) * Number(limit))
.get();
res.json(posts);
} catch (error) {
res.status(500).json({ error: error.message });
}
});
// Create post
app.post('/api/posts', async (req, res) => {
try {
const result = await client.table('posts').create(req.body);
res.status(201).json(result);
} catch (error) {
res.status(500).json({ error: error.message });
}
});
app.listen(3000, () => console.log('Server running on port 3000'));Never hardcode API keys. Use environment variables:
// .env
WOWSQL_PROJECT=myproject
WOWSQL_API_KEY=your-api-key
// app.ts
import WOWSQLClient from '@wowsql/sdk';
const client = new WOWSQLClient({
projectUrl: process.env.WOWSQL_PROJECT!,
apiKey: process.env.WOWSQL_API_KEY!
});Create a single client instance:
// lib/WOWSQL.ts
import WOWSQLClient from '@wowsql/sdk';
export const db = new WOWSQLClient({
projectUrl: process.env.WOWSQL_PROJECT!,
apiKey: process.env.WOWSQL_API_KEY!
});
// Use in other files
import { db } from './lib/WOWSQL';
const users = await db.table('users').get();Define interfaces for your data:
// types/database.ts
export interface User {
id: number;
name: string;
email: string;
created_at: string;
}
export interface Post {
id: number;
user_id: number;
title: string;
content: string;
published_at: string | null;
}
// Use in queries
const users = await db.table<User>('users').get();Always wrap API calls in try-catch:
import { WOWSQLError } from '@wowsql/sdk';
async function createUser(data: any) {
try {
const result = await db.table('users').create(data);
return { success: true, data: result };
} catch (error) {
if (error instanceof WOWSQLError) {
console.error(`Database error: ${error.message}`);
return { success: false, error: error.message };
}
throw error;
}
}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:
import WOWSQLClient from '@wowsql/sdk';
// Using Service Role Key (recommended for server-side, full access)
const client = new WOWSQLClient({
projectUrl: 'myproject',
apiKey: 'wowbase_service_your-service-key-here' // Service Role Key
});
// Using Anonymous Key (for public/client-side access with limited permissions)
const client = new WOWSQLClient({
projectUrl: 'myproject',
apiKey: 'wowbase_anon_your-anon-key-here' // Anonymous Key
});
// Query data
const users = await client.table('users').get();Use Public API Key or Service Role Key for authentication:
import { ProjectAuthClient } from '@wowsql/sdk';
// Using Public API Key (recommended for OAuth, sign-in, sign-up)
const auth = new ProjectAuthClient({
projectUrl: 'myproject',
publicApiKey: 'wowbase_auth_your-public-key-here' // Public API Key
});
// Using Service Role Key (can be used for auth operations too)
const auth = new ProjectAuthClient({
projectUrl: 'myproject',
publicApiKey: 'wowbase_service_your-service-key-here' // Service Role Key
});
// OAuth authentication
const { authorizationUrl } = await auth.getOAuthAuthorizationUrl(
'github',
'https://app.example.com/auth/callback'
);Best practice: Use environment variables for API keys:
// Database operations - Service Role Key
const dbClient = new WOWSQLClient({
projectUrl: process.env.WOWSQL_PROJECT_URL!,
apiKey: process.env.WOWSQL_SERVICE_ROLE_KEY! // or WOWSQL_ANON_KEY
});
// Authentication operations - Public API Key
const authClient = new ProjectAuthClient({
projectUrl: process.env.WOWSQL_PROJECT_URL!,
publicApiKey: process.env.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
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.
import { WOWSQLSchema } from '@wowsql/sdk';
// Initialize schema client with SERVICE ROLE KEY
const schema = new WOWSQLSchema(
'https://your-project.wowsql.com',
'service_xyz789...' // β οΈ Backend only! Never expose!
);// Create a new table
await schema.createTable({
tableName: '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' }
],
primaryKey: 'id',
indexes: [
{ name: 'idx_category', columns: ['category'] },
{ name: 'idx_price', columns: ['price'] }
]
});
console.log('Table created successfully!');// Add a new column
await schema.alterTable({
tableName: 'products',
addColumns: [
{ name: 'stock_quantity', type: 'INT', default: '0' }
]
});
// Modify an existing column
await schema.alterTable({
tableName: 'products',
modifyColumns: [
{ name: 'price', type: 'DECIMAL(12,2)' } // Increase precision
]
});
// Drop a column
await schema.alterTable({
tableName: 'products',
dropColumns: ['category']
});
// Rename a column
await schema.alterTable({
tableName: 'products',
renameColumns: [
{ oldName: 'name', newName: 'product_name' }
]
});// Drop a table
await schema.dropTable('old_table');
// Drop with CASCADE (removes dependent objects)
await schema.dropTable('products', { cascade: true });// Execute custom schema SQL
await schema.executeSQL(`
CREATE INDEX idx_product_name
ON products(product_name);
`);
// Add a foreign key constraint
await 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 (Node.js, Next.js API routes)
- 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/browser code
- Never commit service keys to version control
- Don't use anonymous keys for schema operations (will fail)
// app/api/migrate/route.ts
import { WOWSQLSchema } from '@wowsql/sdk';
import { NextResponse } from 'next/server';
export async function POST() {
const schema = new WOWSQLSchema(
process.env.WOWSQL_PROJECT_URL!,
process.env.WOWSQL_SERVICE_KEY! // From env var
);
try {
// Create users table
await schema.createTable({
tableName: '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' }
],
primaryKey: 'id',
indexes: [{ name: 'idx_email', columns: ['email'] }]
});
return NextResponse.json({ success: true });
} catch (error) {
return NextResponse.json({ error: error.message }, { status: 500 });
}
}import { WOWSQLSchema, PermissionError } from '@wowsql/sdk';
try {
const schema = new WOWSQLSchema(
'https://your-project.wowsql.com',
'service_xyz...'
);
await schema.createTable({
tableName: 'test',
columns: [{ name: 'id', type: 'INT' }]
});
} catch (error) {
if (error instanceof PermissionError) {
console.error('Permission denied:', error.message);
console.error('Make sure you\'re using a SERVICE ROLE KEY!');
} else {
console.error('Error:', error);
}
}Yes! The SDK works in both Node.js and browser environments. However, never expose your Service Role Key in client-side code for production applications. Use Public API Key for authentication or Anonymous Key for limited database access. For full database operations, use a backend proxy.
Rate limits depend on your WOWSQL plan. The SDK will throw a WOWSQLError with status code 429 when rate limits are exceeded.
Currently, the SDK doesn't support transactions directly. Use raw SQL queries for complex transactional operations.
npm update @wowsql/sdk- π§ Email: support@wowsql.com
- π¬ Discord: Join our community
- π Documentation: docs.wowsql.com
- π Issues: GitHub Issues
Contributions are welcome! Please see CONTRIBUTING.md for details.
MIT License - see LICENSE file for details.
See CHANGELOG.md for version history.
Made with β€οΈ by the WOWSQL Team