@wowsql/sdk
v3.8.2
Published
Official TypeScript/JavaScript SDK for WowSQL - PostgreSQL Backend-as-a-Service with S3 Storage, type-safe queries and fluent API
Maintainers
Readme
WowSQL TypeScript SDK
Official TypeScript / Node.js client for WowSQL. All data operations communicate directly with PostgREST over HTTPS — no legacy API layers.
Requirements
- Node.js 16 or later
- TypeScript 4.7 or later (if using TypeScript)
Installation
npm install @wowsql/sdkQuick Start
import WowSQLClient from '@wowsql/sdk';
const client = new WowSQLClient({
projectUrl: 'myproject', // slug, full domain, or full URL
apiKey: 'wowsql_anon_...', // anon or service_role key
});
const { data } = await client.table('users').get();
console.log(data);Configuration
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| projectUrl | string | required | Project slug, domain, or full URL |
| apiKey | string | required | Anonymous or service role API key |
| baseDomain | string | wowsqlconnect.com | Base domain when projectUrl is a slug |
| secure | boolean | true | Use HTTPS |
| timeout | number | 30000 | Request timeout in milliseconds |
projectUrl formats
// Slug only — SDK appends .wowsqlconnect.com
{ projectUrl: 'myproject' }
// Full domain
{ projectUrl: 'myproject.wowsqlconnect.com' }
// Full URL
{ projectUrl: 'https://myproject.wowsqlconnect.com' }API Keys
| Key Type | Prefix | Usage |
|----------|--------|-------|
| Anonymous | wowsql_anon_... | Client-side, public read/write with RLS |
| Service Role | wowsql_service_... | Server-side, full access, bypasses RLS |
Database Operations
Querying Records
// Get all records
const { data, total } = await client.table('products').get();
// Select specific columns
const { data } = await client
.table('products')
.select('id', 'name', 'price')
.get();
// Filter with equality
const { data } = await client
.table('products')
.eq('category', 'electronics')
.get();
// Multiple filters (all are ANDed by default)
const { data } = await client
.table('products')
.gte('price', 100)
.lte('price', 500)
.eq('in_stock', true)
.get();
// OR filter
const { data } = await client
.table('products')
.eq('status', 'active')
.or('status', 'eq', 'featured')
.get();Filter Operators
| Operator | Method | PostgREST |
|----------|--------|-----------|
| Equal | .eq(col, val) | col=eq.val |
| Not equal | .neq(col, val) | col=neq.val |
| Greater than | .gt(col, val) | col=gt.val |
| Greater or equal | .gte(col, val) | col=gte.val |
| Less than | .lt(col, val) | col=lt.val |
| Less or equal | .lte(col, val) | col=lte.val |
| LIKE (case-sensitive) | .like(col, pattern) | col=like.*pattern* |
| ILIKE (case-insensitive) | .ilike(col, pattern) | col=ilike.*pattern* |
| Is null | .isNull(col) | col=is.null |
| Is not null | .isNotNull(col) | col=not.is.null |
| In list | .in(col, [vals]) | col=in.(v1,v2) |
| Not in list | .notIn(col, [vals]) | col=not.in.(v1,v2) |
| Between | .between(col, lo, hi) | col=gte.lo&col=lte.hi |
Ordering and Pagination
// Order results
const { data } = await client
.table('products')
.orderBy('price', 'desc')
.get();
// Multiple column ordering
const { data } = await client.table('products').get({
order: [
{ column: 'category', direction: 'asc' },
{ column: 'price', direction: 'desc' },
],
});
// Limit and offset
const { data } = await client
.table('products')
.limit(20)
.offset(40)
.get();
// Paginate
const result = await client.table('products').paginate(2, 20);
// result.data, result.page, result.perPage, result.total, result.totalPages
// result.hasNext, result.hasPrevSingle Record Fetch
// By primary key
const user = await client.table('users').getById('uuid-here');
// First matching record
const user = await client
.table('users')
.eq('email', '[email protected]')
.first();
// Exactly one record — throws if 0 or more than 1 result
const user = await client
.table('users')
.eq('email', '[email protected]')
.single();Creating Records
// Single record
const { id } = await client.table('users').create({
email: '[email protected]',
name: 'New User',
});
// Alias
const { id } = await client.table('users').insert({
email: '[email protected]',
});
// Bulk insert
const results = await client.table('users').bulkInsert([
{ email: '[email protected]', name: 'Alice' },
{ email: '[email protected]', name: 'Bob' },
]);Updating Records
// Update by primary key
const { affected_rows } = await client.table('users').update('user-uuid', {
name: 'Updated Name',
});
// Update with filters via QueryBuilder
const result = await client
.table('products')
.eq('category', 'old-category')
.update({ category: 'new-category' });Deleting Records
// Delete by primary key
const { affected_rows } = await client.table('users').delete('user-uuid');
// Delete with filters
const result = await client
.table('orders')
.eq('status', 'cancelled')
.lt('created_at', '2024-01-01')
.delete();Upsert
const { id } = await client.table('users').upsert(
{ id: 'existing-uuid', name: 'Updated' },
'id' // conflict column, default: 'id'
);Aggregates
// Count
const total = await client.table('orders').eq('status', 'active').count();
// Sum
const revenue = await client.table('orders').eq('status', 'paid').sum('amount');
// Average
const avgPrice = await client.table('products').eq('category', 'electronics').avg('price');Group By
const { data } = await client.table('orders').get({
select: ['status', 'count(*)'],
group_by: ['status'],
});Authentication
import { ProjectAuthClient } from '@wowsql/sdk';
const auth = new ProjectAuthClient({
projectUrl: 'myproject',
apiKey: 'wowsql_anon_...',
});Email / Password
// Sign up
const { session, user } = await auth.signUp({
email: '[email protected]',
password: 'secure-password',
fullName: 'Jane Doe',
});
// Sign in
const { session, user } = await auth.signIn({
email: '[email protected]',
password: 'secure-password',
});
// Get current user
const user = await auth.getUser(session.access_token);
// Sign out
await auth.signOut(session.access_token);OAuth
// Step 1 — get redirect URL
const { authorization_url } = await auth.getOAuthAuthorizationUrl({
provider: 'google',
redirectUri: 'https://myapp.com/auth/callback',
});
// Redirect user to authorization_url
// Step 2 — after OAuth callback, exchange code for tokens
const { session, user } = await auth.exchangeOAuthCode({
provider: 'google',
code: req.query.code,
});Token Management
// Refresh tokens
const { session } = await auth.refreshToken(session.refresh_token);
// Password reset
await auth.forgotPassword({ email: '[email protected]' });
await auth.resetPassword({ token: 'reset-token', password: 'new-password' });
// Change password (authenticated)
await auth.changePassword({
currentPassword: 'old',
newPassword: 'new',
accessToken: session.access_token,
});OTP and Magic Links
// OTP login
await auth.sendOtp({ email: '[email protected]' });
const { session } = await auth.verifyOtp({ email: '[email protected]', otp: '123456' });
// Magic link
await auth.sendMagicLink({ email: '[email protected]' });File Storage
import { WowSQLStorage } from '@wowsql/sdk';
const storage = new WowSQLStorage({
projectUrl: 'myproject',
apiKey: 'wowsql_anon_...',
});
// Create bucket
await storage.createBucket('avatars', { public: true });
// Upload file (Node.js Buffer or browser File/Blob)
const result = await storage.upload('avatars', 'user-123/avatar.png', fileBuffer, {
contentType: 'image/png',
});
// Get public URL
const url = storage.getPublicUrl('avatars', 'user-123/avatar.png');
// List files
const { files } = await storage.listFiles('avatars', { prefix: 'user-123/' });
// Download
const bytes = await storage.download('avatars', 'user-123/avatar.png');
// Delete
await storage.deleteFile('avatars', 'user-123/avatar.png');Schema Management
Requires a service role key.
import { WowSQLSchema } from '@wowsql/sdk';
const schema = new WowSQLSchema({
projectUrl: 'myproject',
serviceKey: 'wowsql_service_...',
});
// Create table
await schema.createTable('products', [
{ name: 'id', type: 'UUID', primaryKey: true, autoIncrement: 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' },
]);
// Add column
await schema.addColumn('products', 'sku', 'VARCHAR(100)');
// Drop column
await schema.dropColumn('products', 'old_column');
// List tables
const tables = await schema.listTables();
// Get table schema
const tableSchema = await schema.getTableSchema('products');
// Execute raw SQL (DDL only)
await schema.executeSql('CREATE INDEX ON products(name)');
// Drop table
await schema.dropTable('products');Error Handling
import { WOWSQLError } from '@wowsql/sdk';
try {
const { data } = await client.table('orders').eq('id', 'uuid').get();
} catch (err) {
if (err instanceof WOWSQLError) {
console.error(err.message); // Human-readable message
console.error(err.statusCode); // HTTP status code
console.error(err.response); // Raw PostgREST error object
}
}TypeScript Generics
interface Product {
id: string;
name: string;
price: number;
in_stock: boolean;
}
const { data } = await client.table<Product>('products').eq('in_stock', true).get();
// data is typed as Product[]Architecture
All requests are sent directly to the PostgREST endpoint (/rest/v1) exposed by your project's WowSQL pod. There is no intermediate backend — filters, aggregates, ordering, and pagination are translated directly into PostgREST query parameters.
| Operation | HTTP Method | PostgREST Path |
|-----------|-------------|----------------|
| GET | GET | /{table}?col=op.val |
| CREATE | POST | /{table} with Prefer: return=representation |
| UPDATE | PATCH | /{table}?id=eq.{id} with Prefer: return=representation |
| DELETE | DELETE | /{table}?id=eq.{id} with Prefer: return=representation |
| UPSERT | POST | /{table} with Prefer: resolution=merge-duplicates |
| COUNT | GET | /{table}?limit=0 with Prefer: count=exact |
License
MIT
