@wowsql/sdk 3.7.0 → 3.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,333 +1,332 @@
1
- # WOWSQL TypeScript SDK
1
+ # WowSQL TypeScript/JavaScript SDK
2
2
 
3
- Official TypeScript/JavaScript SDK for WOWSQL - The powerful MySQL Backend-as-a-Service platform.
3
+ Official TypeScript/JavaScript client for [WowSQL](https://wowsql.com) - PostgreSQL Backend-as-a-Service with built-in Authentication and Storage.
4
4
 
5
- [![npm version](https://badge.fury.io/js/%40WOWSQL%2Fsdk.svg)](https://www.npmjs.com/package/@wowsql/sdk)
5
+ [![npm version](https://badge.fury.io/js/wowsql.svg)](https://www.npmjs.com/package/wowsql)
6
6
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
7
 
8
- ## Features
9
-
10
- - 🚀 **Zero Configuration** - Get started in seconds
11
- - 🔒 **Type-Safe** - Full TypeScript support with generics
12
- - 🎯 **Fluent API** - Intuitive query builder pattern
13
- - ⚡ **Lightweight** - Minimal dependencies (only axios)
14
- - 🛡️ **Error Handling** - Comprehensive error messages
15
- - 📦 **Tree-Shakeable** - Import only what you need
16
-
17
8
  ## Installation
18
9
 
19
10
  ```bash
20
11
  # npm
21
- npm install @wowsql/sdk
12
+ npm install wowsql
22
13
 
23
14
  # yarn
24
- yarn add @wowsql/sdk
15
+ yarn add wowsql
25
16
 
26
17
  # pnpm
27
- pnpm add @wowsql/sdk
18
+ pnpm add wowsql
28
19
  ```
29
20
 
30
21
  ## Quick Start
31
22
 
23
+ ### Database Operations
24
+
32
25
  ```typescript
33
- import WOWSQLClient from '@wowsql/sdk';
26
+ import { WowSQLClient } from 'wowsql';
34
27
 
35
28
  // Initialize client
36
- const client = new WOWSQLClient({
37
- projectUrl: 'myproject', // Your project subdomain
38
- apiKey: 'your-api-key-here'
29
+ const client = new WowSQLClient({
30
+ projectUrl: 'https://your-project.wowsql.com',
31
+ apiKey: 'your-api-key'
39
32
  });
40
33
 
41
- // Query data
42
- const users = await client.table('users')
43
- .select(['id', 'name', 'email'])
44
- .filter({ column: 'age', operator: 'gt', value: 18 })
45
- .order('created_at', 'desc')
46
- .limit(10)
47
- .get();
48
-
49
- console.log(users.data); // Array of user records
50
- ```
51
-
52
- ## Project Authentication
34
+ // Select data
35
+ const response = await client.table('users').select('*').limit(10).get();
36
+ console.log(response.data); // [{ id: 1, name: 'John', ... }, ...]
53
37
 
54
- The 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.
55
-
56
- ```typescript
57
- import { ProjectAuthClient } from '@wowsql/sdk';
58
-
59
- const auth = new ProjectAuthClient({
60
- projectUrl: 'myproject', // or https://myproject.wowsql.com
61
- apiKey: 'your-anon-key' // Use anon key for client-side, service key for server-side
38
+ // Insert data
39
+ const result = await client.table('users').create({
40
+ name: 'Jane Doe',
41
+ email: 'jane@example.com',
42
+ age: 25
62
43
  });
63
- ```
64
44
 
65
- ### Sign Up Users
66
-
67
- ```typescript
68
- const { user, session } = await auth.signUp({
69
- email: 'user@example.com',
70
- password: 'SuperSecret123',
71
- full_name: 'Demo User',
72
- user_metadata: { referrer: 'landing-page' }
73
- });
45
+ // Update data
46
+ await client.table('users').update(1, { name: 'Jane Smith' });
74
47
 
75
- console.log('New auth user id:', user?.id);
76
- console.log('Access token:', session.accessToken);
48
+ // Delete data
49
+ await client.table('users').delete(1);
77
50
  ```
78
51
 
79
- ### Sign In & Persist Sessions
52
+ ### Storage Operations
80
53
 
81
54
  ```typescript
82
- const { session } = await auth.signIn({
83
- email: 'user@example.com',
84
- password: 'SuperSecret123'
85
- });
55
+ import { WowSQLStorage } from 'wowsql';
86
56
 
87
- // Save the session and reuse on page refresh
88
- auth.setSession({
89
- accessToken: session.accessToken,
90
- refreshToken: session.refreshToken
57
+ const storage = new WowSQLStorage({
58
+ projectUrl: 'https://your-project.wowsql.com',
59
+ apiKey: 'your-api-key'
91
60
  });
92
61
 
93
- const currentUser = await auth.getUser(); // reads the stored token
94
- console.log('Welcome back,', currentUser.full_name);
95
- ```
96
-
97
- ### OAuth Authentication
98
-
99
- Complete OAuth flow with callback handling:
100
-
101
- ```typescript
102
- // Step 1: Get authorization URL
103
- const { authorizationUrl } = await auth.getOAuthAuthorizationUrl(
104
- 'github',
105
- 'https://app.your-domain.com/auth/callback'
106
- );
107
-
108
- window.location.href = authorizationUrl;
62
+ // Create a bucket
63
+ await storage.createBucket('avatars', { public: true });
109
64
 
110
- // Step 2: After user authorizes, exchange code for tokens
111
- // (In your callback handler)
112
- const result = await auth.exchangeOAuthCallback(
113
- 'github',
114
- code, // from URL query params
115
- 'https://app.your-domain.com/auth/callback'
116
- );
65
+ // Upload a file
66
+ await storage.upload('avatars', fileData, 'users/avatar.png');
117
67
 
118
- console.log('Logged in as:', result.user?.email);
119
- console.log('Access token:', result.session.accessToken);
120
- ```
68
+ // Get public URL
69
+ const url = storage.getPublicUrl('avatars', 'users/avatar.png');
121
70
 
122
- ### Password Reset
71
+ // List files
72
+ const files = await storage.listFiles('avatars', { prefix: 'users/' });
123
73
 
124
- ```typescript
125
- // Request password reset
126
- const forgotResult = await auth.forgotPassword('user@example.com');
127
- console.log(forgotResult.message); // "If that email exists, a password reset link has been sent"
74
+ // Download a file
75
+ const data = await storage.download('avatars', 'users/avatar.png');
128
76
 
129
- // Reset password (after user clicks email link)
130
- const resetResult = await auth.resetPassword(
131
- token, // from email link
132
- 'newSecurePassword123'
133
- );
134
- console.log(resetResult.message); // "Password reset successfully! You can now login with your new password"
77
+ // Check quota
78
+ const quota = await storage.getQuota();
79
+ console.log(`Used: ${quota.used_gb} GB / ${quota.total_gb} GB`);
135
80
  ```
136
81
 
137
- ### Session Management
138
-
139
- The client exposes `getSession`, `setSession`, and `clearSession` utilities so you can wire tokens into your own persistence layer (localStorage, cookies, etc.):
82
+ ### Authentication
140
83
 
141
84
  ```typescript
142
- // Get current session
143
- const session = auth.getSession();
144
- console.log('Access token:', session.accessToken);
145
-
146
- // Set session (e.g., from localStorage on page load)
147
- auth.setSession({
148
- accessToken: localStorage.getItem('access_token')!,
149
- refreshToken: localStorage.getItem('refresh_token')!
150
- });
151
-
152
- // Clear session (logout)
153
- auth.clearSession();
154
- ```
155
-
156
- ## Configuration
157
-
158
- ### Basic Configuration
85
+ import { ProjectAuthClient } from 'wowsql';
159
86
 
160
- ```typescript
161
- const client = new WOWSQLClient({
162
- projectUrl: 'myproject', // Your project subdomain
163
- apiKey: 'your-api-key' // Your API key from dashboard
87
+ const auth = new ProjectAuthClient({
88
+ projectUrl: 'https://your-project.wowsql.com',
89
+ apiKey: 'your-anon-key'
164
90
  });
165
- ```
166
-
167
- ### Advanced Configuration
168
91
 
169
- ```typescript
170
- const client = new WOWSQLClient({
171
- projectUrl: 'myproject',
172
- apiKey: 'your-api-key',
173
- baseDomain: 'wowsql.com', // Custom domain (optional)
174
- secure: true, // Use HTTPS (default: true)
175
- timeout: 30000 // Request timeout in ms (default: 30000)
92
+ // Sign up
93
+ const { user, session } = await auth.signUp({
94
+ email: 'user@example.com',
95
+ password: 'SuperSecret123',
96
+ fullName: 'Demo User'
176
97
  });
177
- ```
178
-
179
- ### Using Full URL
180
98
 
181
- ```typescript
182
- const client = new WOWSQLClient({
183
- projectUrl: 'https://myproject.wowsql.com',
184
- apiKey: 'your-api-key'
99
+ // Sign in
100
+ const result = await auth.signIn({
101
+ email: 'user@example.com',
102
+ password: 'SuperSecret123'
185
103
  });
104
+ console.log('Access token:', result.session.accessToken);
186
105
  ```
187
106
 
188
- ## Usage Examples
107
+ ## Features
189
108
 
190
- ### TypeScript with Generics
109
+ ### Database Features
110
+ - Full CRUD operations (Create, Read, Update, Delete)
111
+ - Advanced filtering (eq, neq, gt, gte, lt, lte, like, isNull, isNotNull, in, notIn, between, notBetween)
112
+ - Logical operators (AND, OR)
113
+ - GROUP BY and aggregate functions (COUNT, SUM, AVG, MAX, MIN)
114
+ - HAVING clause for filtering aggregated results
115
+ - Multiple ORDER BY columns
116
+ - Date/time functions in SELECT and filters
117
+ - Expressions in SELECT (e.g., `COUNT(*)`, `DATE(created_at) as date`)
118
+ - Pagination (limit, offset, paginate)
119
+ - Sorting (orderBy)
120
+ - Get record by ID
121
+ - Bulk insert
122
+ - Upsert (insert or update on conflict)
123
+ - Table schema introspection
124
+ - Raw SQL queries
125
+ - Full TypeScript generics support
126
+ - Health check endpoint
127
+
128
+ ### Authentication Features
129
+ - Email/password sign up and sign in
130
+ - OAuth provider support (GitHub, Google, etc.)
131
+ - Password reset flow (forgot + reset)
132
+ - OTP (one-time password) via email
133
+ - Magic link authentication
134
+ - Email verification and resend
135
+ - Session management (get, set, clear, refresh)
136
+ - Change password
137
+ - Update user profile
138
+ - Custom token storage
139
+
140
+ ### Storage Features
141
+ - Bucket management (create, list, get, update, delete)
142
+ - File upload from data or local path
143
+ - File download to memory or local path
144
+ - File listing with prefix and pagination
145
+ - File deletion
146
+ - Public URL generation
147
+ - Storage statistics and quota management
148
+
149
+ ## Database Operations
150
+
151
+ ### Select Queries
191
152
 
192
153
  ```typescript
193
- interface User {
194
- id: number;
195
- name: string;
196
- email: string;
197
- age: number;
198
- created_at: string;
199
- }
154
+ import { WowSQLClient } from 'wowsql';
200
155
 
201
- const client = new WOWSQLClient({
202
- projectUrl: 'myproject',
156
+ const client = new WowSQLClient({
157
+ projectUrl: 'https://your-project.wowsql.com',
203
158
  apiKey: 'your-api-key'
204
159
  });
205
160
 
206
- // Type-safe queries
207
- const users = await client.table<User>('users').get();
208
- users.data.forEach(user => {
209
- console.log(user.name); // Type-safe!
210
- });
211
- ```
161
+ // Select all columns
162
+ const users = await client.table('users').select('*').get();
212
163
 
213
- ### Create Records
164
+ // Select specific columns
165
+ const users = await client.table('users')
166
+ .select('id', 'name', 'email')
167
+ .get();
214
168
 
215
- ```typescript
216
- // Create a single user
217
- const result = await client.table('users').create({
218
- name: 'John Doe',
219
- email: 'john@example.com',
220
- age: 25
221
- });
169
+ // With filters
170
+ const activeUsers = await client.table('users')
171
+ .select('*')
172
+ .eq('status', 'active')
173
+ .gt('age', 18)
174
+ .get();
222
175
 
223
- console.log(result.id); // New record ID
224
- ```
176
+ // With ordering
177
+ const recentUsers = await client.table('users')
178
+ .select('*')
179
+ .orderBy('created_at', 'desc')
180
+ .limit(10)
181
+ .get();
225
182
 
226
- ### Read Records
183
+ // With pagination
184
+ const page1 = await client.table('users').select('*').limit(20).offset(0).get();
185
+ const page2 = await client.table('users').select('*').limit(20).offset(20).get();
186
+
187
+ // Using paginate helper
188
+ const paginated = await client.table('users').paginate(1, 20);
189
+ console.log(paginated.data); // records for page 1
190
+ console.log(paginated.total); // total record count
191
+ console.log(paginated.page); // current page
192
+ console.log(paginated.perPage); // items per page
193
+
194
+ // Pattern matching
195
+ const gmailUsers = await client.table('users')
196
+ .select('*')
197
+ .like('email', '%@gmail.com')
198
+ .get();
227
199
 
228
- ```typescript
229
- // Get all records
230
- const allUsers = await client.table('users').get();
200
+ // IN operator
201
+ const categories = await client.table('products')
202
+ .select('*')
203
+ .in('category', ['electronics', 'books', 'clothing'])
204
+ .get();
231
205
 
232
- // Get by ID
233
- const user = await client.table('users').getById(1);
206
+ // BETWEEN operator
207
+ const priceRange = await client.table('products')
208
+ .select('*')
209
+ .between('price', [10, 100])
210
+ .get();
234
211
 
235
- // Select specific columns
236
- const users = await client.table('users')
237
- .select(['id', 'name', 'email'])
212
+ // NOT IN operator
213
+ const active = await client.table('products')
214
+ .select('*')
215
+ .notIn('status', ['deleted', 'archived'])
238
216
  .get();
239
217
 
240
- // With filters
241
- const adults = await client.table('users')
242
- .filter({ column: 'age', operator: 'gte', value: 18 })
218
+ // NOT BETWEEN operator
219
+ const outliers = await client.table('products')
220
+ .select('*')
221
+ .notBetween('price', [10, 100])
243
222
  .get();
244
223
 
245
- // Multiple filters
246
- const result = await client.table('users')
247
- .filter({ column: 'age', operator: 'gte', value: 18 })
248
- .filter({ column: 'country', operator: 'eq', value: 'USA' })
224
+ // IS NULL / IS NOT NULL
225
+ const noEmail = await client.table('users')
226
+ .select('*')
227
+ .isNull('email')
249
228
  .get();
250
229
 
251
- // With sorting
252
- const sorted = await client.table('users')
253
- .order('created_at', 'desc')
230
+ const hasEmail = await client.table('users')
231
+ .select('*')
232
+ .isNotNull('email')
254
233
  .get();
255
234
 
256
- // With pagination
257
- const page1 = await client.table('users')
258
- .limit(10)
259
- .offset(0)
235
+ // OR conditions
236
+ const results = await client.table('products')
237
+ .select('*')
238
+ .filter('category', 'eq', 'electronics', 'AND')
239
+ .or('price', 'gt', 1000)
260
240
  .get();
261
241
 
262
- // Get first record
242
+ // Get first record only
263
243
  const firstUser = await client.table('users')
264
- .filter({ column: 'email', operator: 'eq', value: 'john@example.com' })
244
+ .select('*')
245
+ .eq('email', 'john@example.com')
265
246
  .first();
247
+
248
+ // Get single record (throws if not exactly one)
249
+ const singleUser = await client.table('users')
250
+ .select('*')
251
+ .eq('id', 1)
252
+ .single();
253
+
254
+ // Count records
255
+ const totalUsers = await client.table('users').count();
256
+ console.log(`Total users: ${totalUsers}`);
257
+
258
+ // Sum a column
259
+ const totalRevenue = await client.table('orders')
260
+ .select('*')
261
+ .eq('status', 'completed')
262
+ .sum('total');
263
+
264
+ // Average a column
265
+ const avgPrice = await client.table('products')
266
+ .select('*')
267
+ .avg('price');
266
268
  ```
267
269
 
268
- ### Update Records
270
+ ### Insert Data
269
271
 
270
272
  ```typescript
271
- // Update by ID
272
- const result = await client.table('users').update(1, {
273
- name: 'Jane Doe',
274
- age: 26
273
+ // Insert a single record
274
+ const result = await client.table('users').create({
275
+ name: 'John Doe',
276
+ email: 'john@example.com',
277
+ age: 30
278
+ });
279
+ console.log(result.id); // new record ID
280
+
281
+ // Using the insert alias
282
+ const result = await client.table('users').insert({
283
+ name: 'Alice',
284
+ email: 'alice@example.com'
275
285
  });
276
286
 
277
- console.log(result.affected_rows); // Number of rows updated
287
+ // Bulk insert multiple records
288
+ const results = await client.table('users').bulkInsert([
289
+ { name: 'Alice', email: 'alice@example.com', age: 28 },
290
+ { name: 'Bob', email: 'bob@example.com', age: 32 },
291
+ { name: 'Carol', email: 'carol@example.com', age: 24 }
292
+ ]);
293
+ console.log(`Inserted ${results.length} records`);
278
294
  ```
279
295
 
280
- ### Delete Records
296
+ ### Upsert Data
281
297
 
282
298
  ```typescript
283
- // Delete by ID
284
- const result = await client.table('users').delete(1);
299
+ // Insert or update on conflict (default conflict column: id)
300
+ const result = await client.table('users').upsert({
301
+ id: 1,
302
+ name: 'John Updated',
303
+ email: 'john@example.com'
304
+ });
285
305
 
286
- console.log(result.affected_rows); // Number of rows deleted
306
+ // Specify conflict column
307
+ const result = await client.table('users').upsert(
308
+ { email: 'john@example.com', name: 'John Updated' },
309
+ 'email'
310
+ );
287
311
  ```
288
312
 
289
- ### Filter Operators
313
+ ### Update Data
290
314
 
291
315
  ```typescript
292
- // Equal
293
- .filter({ column: 'status', operator: 'eq', value: 'active' })
294
-
295
- // Not equal
296
- .filter({ column: 'status', operator: 'neq', value: 'deleted' })
297
-
298
- // Greater than
299
- .filter({ column: 'age', operator: 'gt', value: 18 })
300
-
301
- // Greater than or equal
302
- .filter({ column: 'age', operator: 'gte', value: 18 })
303
-
304
- // Less than
305
- .filter({ column: 'price', operator: 'lt', value: 100 })
306
-
307
- // Less than or equal
308
- .filter({ column: 'price', operator: 'lte', value: 100 })
309
-
310
- // Like (pattern matching)
311
- .filter({ column: 'name', operator: 'like', value: '%John%' })
312
-
313
- // Is null
314
- .filter({ column: 'deleted_at', operator: 'is', value: null })
315
-
316
- // IN operator (value must be an array)
317
- .filter('category', 'in', ['electronics', 'books', 'clothing'])
318
-
319
- // NOT IN operator
320
- .filter('status', 'not_in', ['deleted', 'archived'])
321
-
322
- // BETWEEN operator (value must be an array of 2 values)
323
- .filter('price', 'between', [10, 100])
316
+ // Update by ID
317
+ const result = await client.table('users').update(1, {
318
+ name: 'Jane Smith',
319
+ age: 26
320
+ });
321
+ console.log(result.affected_rows);
322
+ ```
324
323
 
325
- // NOT BETWEEN operator
326
- .filter('age', 'not_between', [18, 65])
324
+ ### Delete Data
327
325
 
328
- // OR logical operator
329
- .filter('category', 'eq', 'electronics', 'AND')
330
- .filter('price', 'gt', 1000, 'OR') // OR condition
326
+ ```typescript
327
+ // Delete by ID
328
+ const result = await client.table('users').delete(1);
329
+ console.log(result.affected_rows);
331
330
  ```
332
331
 
333
332
  ## Advanced Query Features
@@ -340,93 +339,198 @@ GROUP BY supports both simple column names and SQL expressions with functions. A
340
339
 
341
340
  ```typescript
342
341
  // Group by single column
343
- const result = await client.table("products")
344
- .select("category", "COUNT(*) as count", "AVG(price) as avg_price")
345
- .groupBy("category")
342
+ const result = await client.table('products')
343
+ .select('category', 'COUNT(*) as count', 'AVG(price) as avg_price')
344
+ .groupBy('category')
346
345
  .get();
347
346
 
348
347
  // Group by multiple columns
349
- const result = await client.table("sales")
350
- .select("region", "category", "SUM(amount) as total")
351
- .groupBy(["region", "category"])
348
+ const result = await client.table('sales')
349
+ .select('region', 'category', 'SUM(amount) as total')
350
+ .groupBy(['region', 'category'])
352
351
  .get();
353
352
  ```
354
353
 
355
354
  #### GROUP BY with Date/Time Functions
356
355
 
357
356
  ```typescript
358
- // Group by date
359
- const result = await client.table("orders")
360
- .select("DATE(created_at) as date", "COUNT(*) as orders", "SUM(total) as revenue")
361
- .groupBy("DATE(created_at)")
362
- .orderBy("date", "desc")
357
+ // Group by date (extract date part)
358
+ const result = await client.table('orders')
359
+ .select('DATE(created_at) as date', 'COUNT(*) as orders', 'SUM(total) as revenue')
360
+ .groupBy('DATE(created_at)')
361
+ .orderBy('date', 'desc')
362
+ .get();
363
+
364
+ // Group by year
365
+ const result = await client.table('orders')
366
+ .select('YEAR(created_at) as year', 'COUNT(*) as orders')
367
+ .groupBy('YEAR(created_at)')
363
368
  .get();
364
369
 
365
370
  // Group by year and month
366
- const result = await client.table("orders")
367
- .select("YEAR(created_at) as year", "MONTH(created_at) as month", "SUM(total) as revenue")
368
- .groupBy(["YEAR(created_at)", "MONTH(created_at)"])
371
+ const result = await client.table('orders')
372
+ .select(
373
+ 'YEAR(created_at) as year',
374
+ 'MONTH(created_at) as month',
375
+ 'SUM(total) as revenue'
376
+ )
377
+ .groupBy(['YEAR(created_at)', 'MONTH(created_at)'])
378
+ .orderBy('year', 'desc')
379
+ .orderBy('month', 'desc')
369
380
  .get();
370
381
 
371
382
  // Group by week
372
- const result = await client.table("orders")
373
- .select("WEEK(created_at) as week", "COUNT(*) as orders")
374
- .groupBy("WEEK(created_at)")
383
+ const result = await client.table('orders')
384
+ .select('WEEK(created_at) as week', 'COUNT(*) as orders')
385
+ .groupBy('WEEK(created_at)')
386
+ .get();
387
+
388
+ // Group by quarter
389
+ const result = await client.table('orders')
390
+ .select('QUARTER(created_at) as quarter', 'SUM(total) as revenue')
391
+ .groupBy('QUARTER(created_at)')
392
+ .get();
393
+ ```
394
+
395
+ #### GROUP BY with String Functions
396
+
397
+ ```typescript
398
+ // Group by first letter of name
399
+ const result = await client.table('users')
400
+ .select('LEFT(name, 1) as first_letter', 'COUNT(*) as count')
401
+ .groupBy('LEFT(name, 1)')
402
+ .get();
403
+
404
+ // Group by uppercase category
405
+ const result = await client.table('products')
406
+ .select('UPPER(category) as category_upper', 'COUNT(*) as count')
407
+ .groupBy('UPPER(category)')
408
+ .get();
409
+ ```
410
+
411
+ #### GROUP BY with Mathematical Functions
412
+
413
+ ```typescript
414
+ // Group by rounded price ranges
415
+ const result = await client.table('products')
416
+ .select('ROUND(price, -1) as price_range', 'COUNT(*) as count')
417
+ .groupBy('ROUND(price, -1)')
418
+ .get();
419
+
420
+ // Group by price tier (using FLOOR)
421
+ const result = await client.table('products')
422
+ .select('FLOOR(price / 10) * 10 as price_tier', 'COUNT(*) as count')
423
+ .groupBy('FLOOR(price / 10) * 10')
375
424
  .get();
376
425
  ```
377
426
 
378
427
  #### Supported Functions in GROUP BY
379
428
 
380
- **Date/Time:** `DATE()`, `YEAR()`, `MONTH()`, `DAY()`, `WEEK()`, `QUARTER()`, `HOUR()`, `MINUTE()`, `SECOND()`, `DATE_FORMAT()`, `DATE_ADD()`, `DATE_SUB()`, `DATEDIFF()`, `NOW()`, `CURRENT_TIMESTAMP()`, etc.
429
+ The following functions are allowed in GROUP BY expressions:
430
+
431
+ **Date/Time Functions:**
432
+ - `DATE()`, `YEAR()`, `MONTH()`, `DAY()`, `DAYOFMONTH()`, `DAYOFWEEK()`, `DAYOFYEAR()`
433
+ - `WEEK()`, `QUARTER()`, `HOUR()`, `MINUTE()`, `SECOND()`
434
+ - `DATE_FORMAT()`, `TIME()`, `DATE_ADD()`, `DATE_SUB()`
435
+ - `DATEDIFF()`, `TIMEDIFF()`, `TIMESTAMPDIFF()`
436
+ - `NOW()`, `CURRENT_TIMESTAMP()`, `CURDATE()`, `CURRENT_DATE()`
437
+ - `CURTIME()`, `CURRENT_TIME()`, `UNIX_TIMESTAMP()`
438
+
439
+ **String Functions:**
440
+ - `CONCAT()`, `CONCAT_WS()`, `SUBSTRING()`, `SUBSTR()`, `LEFT()`, `RIGHT()`
441
+ - `LENGTH()`, `CHAR_LENGTH()`, `UPPER()`, `LOWER()`, `TRIM()`, `LTRIM()`, `RTRIM()`
442
+ - `REPLACE()`, `LOCATE()`, `POSITION()`
381
443
 
382
- **String:** `CONCAT()`, `SUBSTRING()`, `LEFT()`, `RIGHT()`, `UPPER()`, `LOWER()`, `LENGTH()`, `TRIM()`, etc.
444
+ **Mathematical Functions:**
445
+ - `ABS()`, `ROUND()`, `CEIL()`, `CEILING()`, `FLOOR()`, `POW()`, `POWER()`, `SQRT()`, `MOD()`, `RAND()`
383
446
 
384
- **Mathematical:** `ROUND()`, `CEIL()`, `FLOOR()`, `ABS()`, `POW()`, `SQRT()`, `MOD()`, etc.
447
+ > All expressions are validated for security. Only whitelisted functions are allowed.
385
448
 
386
449
  ### HAVING Clause
387
450
 
388
- HAVING filters aggregated results after GROUP BY. Supports aggregate functions and comparison operators.
451
+ HAVING is used to filter aggregated results after GROUP BY. It supports aggregate functions and comparison operators.
452
+
453
+ #### Basic HAVING
389
454
 
390
455
  ```typescript
391
456
  // Filter aggregated results
392
- const result = await client.table("products")
393
- .select("category", "COUNT(*) as count")
394
- .groupBy("category")
395
- .having("COUNT(*)", "gt", 10)
457
+ const result = await client.table('products')
458
+ .select('category', 'COUNT(*) as count')
459
+ .groupBy('category')
460
+ .having('COUNT(*)', 'gt', 10)
461
+ .get();
462
+
463
+ // Multiple HAVING conditions (AND logic)
464
+ const result = await client.table('orders')
465
+ .select('DATE(created_at) as date', 'SUM(total) as revenue')
466
+ .groupBy('DATE(created_at)')
467
+ .having('SUM(total)', 'gt', 1000)
468
+ .having('COUNT(*)', 'gte', 5)
469
+ .get();
470
+ ```
471
+
472
+ #### HAVING with Aggregate Functions
473
+
474
+ ```typescript
475
+ // Filter by average
476
+ const result = await client.table('products')
477
+ .select('category', 'AVG(price) as avg_price', 'COUNT(*) as count')
478
+ .groupBy('category')
479
+ .having('AVG(price)', 'gt', 100)
480
+ .having('COUNT(*)', 'gte', 5)
396
481
  .get();
397
482
 
398
- // Multiple HAVING conditions
399
- const result = await client.table("orders")
400
- .select("DATE(created_at) as date", "SUM(total) as revenue")
401
- .groupBy("DATE(created_at)")
402
- .having("SUM(total)", "gt", 1000)
403
- .having("COUNT(*)", "gte", 5)
483
+ // Filter by sum
484
+ const result = await client.table('orders')
485
+ .select('customer_id', 'SUM(total) as total_spent')
486
+ .groupBy('customer_id')
487
+ .having('SUM(total)', 'gt', 1000)
404
488
  .get();
405
489
 
406
- // HAVING with different aggregate functions
407
- const result = await client.table("products")
408
- .select("category", "AVG(price) as avg_price", "COUNT(*) as count")
409
- .groupBy("category")
410
- .having("AVG(price)", "gt", 100)
411
- .having("COUNT(*)", "gte", 5)
490
+ // Filter by max/min
491
+ const result = await client.table('products')
492
+ .select('category', 'MAX(price) as max_price', 'MIN(price) as min_price')
493
+ .groupBy('category')
494
+ .having('MAX(price)', 'gt', 500)
412
495
  .get();
413
496
  ```
414
497
 
415
- **Supported HAVING Operators:** `eq`, `neq`, `gt`, `gte`, `lt`, `lte`
498
+ #### Supported HAVING Operators
416
499
 
417
- **Supported Aggregate Functions:** `COUNT(*)`, `SUM()`, `AVG()`, `MAX()`, `MIN()`, `GROUP_CONCAT()`, `STDDEV()`, `VARIANCE()`
500
+ - `eq` - Equal to
501
+ - `neq` - Not equal to
502
+ - `gt` - Greater than
503
+ - `gte` - Greater than or equal to
504
+ - `lt` - Less than
505
+ - `lte` - Less than or equal to
506
+
507
+ #### Supported Aggregate Functions in HAVING
508
+
509
+ - `COUNT(*)` or `COUNT(column)` - Count of rows
510
+ - `SUM(column)` - Sum of values
511
+ - `AVG(column)` - Average of values
512
+ - `MAX(column)` - Maximum value
513
+ - `MIN(column)` - Minimum value
514
+ - `GROUP_CONCAT(column)` - Concatenated values
515
+ - `STDDEV(column)`, `STDDEV_POP(column)`, `STDDEV_SAMP(column)` - Standard deviation
516
+ - `VARIANCE(column)`, `VAR_POP(column)`, `VAR_SAMP(column)` - Variance
418
517
 
419
518
  ### Multiple ORDER BY
420
519
 
421
520
  ```typescript
422
- // Order by multiple columns
423
- const result = await client.table("products")
424
- .select("*")
425
- .orderByMultiple([
426
- { column: "category", direction: "asc" },
427
- { column: "price", direction: "desc" },
428
- { column: "created_at", direction: "desc" }
429
- ])
521
+ // Chain multiple orderBy calls
522
+ const result = await client.table('products')
523
+ .select('*')
524
+ .orderBy('category', 'asc')
525
+ .orderBy('price', 'desc')
526
+ .orderBy('created_at', 'desc')
527
+ .get();
528
+
529
+ // Using the order alias
530
+ const result = await client.table('products')
531
+ .select('*')
532
+ .order('category', 'asc')
533
+ .order('price', 'desc')
430
534
  .get();
431
535
  ```
432
536
 
@@ -434,37 +538,23 @@ const result = await client.table("products")
434
538
 
435
539
  ```typescript
436
540
  // Filter by date range using functions
437
- const result = await client.table("orders")
438
- .select("*")
439
- .filter("created_at", "gte", "DATE_SUB(NOW(), INTERVAL 7 DAY)")
541
+ const result = await client.table('orders')
542
+ .select('*')
543
+ .filter('created_at', 'gte', 'DATE_SUB(NOW(), INTERVAL 7 DAY)')
440
544
  .get();
441
545
 
442
546
  // Group by date
443
- const result = await client.table("orders")
444
- .select("DATE(created_at) as date", "COUNT(*) as count")
445
- .groupBy("DATE(created_at)")
547
+ const result = await client.table('orders')
548
+ .select('DATE(created_at) as date', 'COUNT(*) as count')
549
+ .groupBy('DATE(created_at)')
446
550
  .get();
447
- ```
448
-
449
- ### Complex Queries
450
551
 
451
- ```typescript
452
- // Combine multiple operations
453
- const result = await client.table<Product>('products')
454
- .select(['id', 'name', 'price', 'category'])
455
- .filter({ column: 'category', operator: 'eq', value: 'Electronics' })
456
- .filter({ column: 'price', operator: 'lt', value: 1000 })
457
- .filter({ column: 'in_stock', operator: 'eq', value: true })
458
- .order('price', 'asc')
459
- .limit(20)
460
- .offset(0)
552
+ // Filter by year/month
553
+ const result = await client.table('orders')
554
+ .select('*')
555
+ .filter('YEAR(created_at)', 'eq', 2024)
556
+ .filter('MONTH(created_at)', 'eq', 1)
461
557
  .get();
462
-
463
- console.log(`Found ${result.total} products`);
464
- console.log(`Showing ${result.count} products`);
465
- result.data.forEach(product => {
466
- console.log(`${product.name}: $${product.price}`);
467
- });
468
558
  ```
469
559
 
470
560
  ### Raw SQL Queries
@@ -472,15 +562,15 @@ result.data.forEach(product => {
472
562
  ```typescript
473
563
  // Execute custom SQL (read-only)
474
564
  const results = await client.query<User>(`
475
- SELECT id, name, email
476
- FROM users
477
- WHERE age > 18
478
- ORDER BY created_at DESC
565
+ SELECT id, name, email
566
+ FROM users
567
+ WHERE age > 18
568
+ ORDER BY created_at DESC
479
569
  LIMIT 10
480
570
  `);
481
571
  ```
482
572
 
483
- ### Database Metadata
573
+ ### Utility Methods
484
574
 
485
575
  ```typescript
486
576
  // List all tables
@@ -489,448 +579,471 @@ console.log(tables); // ['users', 'posts', 'comments']
489
579
 
490
580
  // Get table schema
491
581
  const schema = await client.getTableSchema('users');
492
- console.log(schema.columns);
493
- console.log(schema.primary_key);
494
- ```
582
+ console.log(schema.columns); // [{ name: 'id', type: 'INT', ... }, ...]
583
+ console.log(schema.primary_key); // 'id'
495
584
 
496
- ### Health Check
585
+ // Get record by ID
586
+ const user = await client.table('users').getById(1);
587
+ console.log(user); // { id: 1, name: 'John', ... }
497
588
 
498
- ```typescript
499
- // Check API health
589
+ // Health check
500
590
  const health = await client.health();
501
- console.log(health.status); // 'ok'
591
+ console.log(health.status); // 'ok'
592
+ console.log(health.timestamp); // '2024-01-15T10:30:00Z'
593
+
594
+ // Close the client
595
+ client.close();
502
596
  ```
503
597
 
504
- ## Error Handling
598
+ ## Filter Operators
599
+
600
+ Complete list of all available filter operators with examples:
505
601
 
506
602
  ```typescript
507
- import { WOWSQLClient, WOWSQLError } from '@wowsql/sdk';
603
+ // Equal
604
+ .eq('status', 'active')
508
605
 
509
- try {
510
- const user = await client.table('users').getById(999);
511
- } catch (error) {
512
- if (error instanceof WOWSQLError) {
513
- console.error(`Error ${error.statusCode}: ${error.message}`);
514
- console.error(error.response); // Full error response
515
- } else {
516
- console.error('Unexpected error:', error);
517
- }
518
- }
519
- ```
606
+ // Not equal
607
+ .neq('status', 'deleted')
608
+
609
+ // Greater than
610
+ .gt('age', 18)
520
611
 
521
- ## API Reference
612
+ // Greater than or equal
613
+ .gte('age', 18)
522
614
 
523
- ### WOWSQLClient
615
+ // Less than
616
+ .lt('price', 100)
524
617
 
525
- Main client class for interacting with WOWSQL API.
618
+ // Less than or equal
619
+ .lte('price', 100)
526
620
 
527
- #### Methods
621
+ // Pattern matching (SQL LIKE)
622
+ .like('email', '%@gmail.com')
528
623
 
529
- - `table<T>(tableName: string): Table<T>` - Get table interface
530
- - `listTables(): Promise<string[]>` - List all tables
531
- - `getTableSchema(tableName: string): Promise<TableSchema>` - Get table schema
532
- - `query<T>(sql: string): Promise<T[]>` - Execute raw SQL
533
- - `health(): Promise<{status: string, timestamp: string}>` - Health check
624
+ // IS NULL
625
+ .isNull('deleted_at')
534
626
 
535
- ### Table<T>
627
+ // IS NOT NULL
628
+ .isNotNull('email')
536
629
 
537
- Fluent interface for table operations.
630
+ // IN operator (value must be an array)
631
+ .in('category', ['electronics', 'books', 'clothing'])
538
632
 
539
- #### Methods
633
+ // NOT IN operator
634
+ .notIn('status', ['deleted', 'archived'])
540
635
 
541
- - `select(columns: string | string[]): QueryBuilder<T>` - Select columns
542
- - `filter(filter: FilterExpression): QueryBuilder<T>` - Add filter
543
- - `get(options?: QueryOptions): Promise<QueryResponse<T>>` - Get records
544
- - `getById(id: string | number): Promise<T>` - Get by ID
545
- - `create(data: Partial<T>): Promise<CreateResponse>` - Create record
546
- - `update(id: string | number, data: Partial<T>): Promise<UpdateResponse>` - Update record
547
- - `delete(id: string | number): Promise<DeleteResponse>` - Delete record
636
+ // BETWEEN operator (value must be an array of 2 values)
637
+ .between('price', [10, 100])
548
638
 
549
- ### QueryBuilder<T>
639
+ // NOT BETWEEN operator
640
+ .notBetween('age', [18, 65])
550
641
 
551
- Chainable query builder.
642
+ // OR logical operator
643
+ .filter('category', 'eq', 'electronics', 'AND')
644
+ .filter('price', 'gt', 1000, 'OR')
552
645
 
553
- #### Methods
646
+ // Using the generic filter method
647
+ .filter('column', 'operator', value)
648
+ .filter('column', 'operator', value, 'OR')
649
+ ```
554
650
 
555
- - `select(columns: string | string[]): this` - Select columns
556
- - `filter(filter: FilterExpression): this` - Add filter
557
- - `order(column: string, direction?: 'asc' | 'desc'): this` - Order by
558
- - `limit(limit: number): this` - Limit results
559
- - `offset(offset: number): this` - Skip records
560
- - `get(options?: QueryOptions): Promise<QueryResponse<T>>` - Execute query
561
- - `first(): Promise<T | null>` - Get first record
651
+ ## Authentication
562
652
 
563
- ## Real-World Examples
653
+ The `ProjectAuthClient` provides a complete authentication system for your application.
564
654
 
565
- ### Next.js App Router
655
+ ### Initialization
566
656
 
567
657
  ```typescript
568
- // app/api/users/route.ts
569
- import { NextResponse } from 'next/server';
570
- import WOWSQLClient from '@wowsql/sdk';
658
+ import { ProjectAuthClient } from 'wowsql';
571
659
 
572
- const client = new WOWSQLClient({
573
- projectUrl: process.env.WOWSQL_PROJECT!,
574
- apiKey: process.env.WOWSQL_API_KEY!
660
+ const auth = new ProjectAuthClient({
661
+ projectUrl: 'https://your-project.wowsql.com',
662
+ apiKey: 'your-anon-key'
575
663
  });
576
664
 
577
- export async function GET(request: Request) {
578
- try {
579
- const { searchParams } = new URL(request.url);
580
- const page = parseInt(searchParams.get('page') || '1');
581
- const limit = 20;
582
-
583
- const users = await client.table('users')
584
- .select(['id', 'name', 'email', 'created_at'])
585
- .order('created_at', 'desc')
586
- .limit(limit)
587
- .offset((page - 1) * limit)
588
- .get();
589
-
590
- return NextResponse.json(users);
591
- } catch (error) {
592
- return NextResponse.json(
593
- { error: 'Failed to fetch users' },
594
- { status: 500 }
595
- );
665
+ // With custom token storage (e.g., for React Native)
666
+ const auth = new ProjectAuthClient({
667
+ projectUrl: 'https://your-project.wowsql.com',
668
+ apiKey: 'your-anon-key',
669
+ storage: {
670
+ getItem: (key: string) => AsyncStorage.getItem(key),
671
+ setItem: (key: string, value: string) => AsyncStorage.setItem(key, value),
672
+ removeItem: (key: string) => AsyncStorage.removeItem(key)
596
673
  }
597
- }
674
+ });
675
+ ```
598
676
 
599
- export async function POST(request: Request) {
600
- try {
601
- const body = await request.json();
602
- const result = await client.table('users').create(body);
603
- return NextResponse.json(result, { status: 201 });
604
- } catch (error) {
605
- return NextResponse.json(
606
- { error: 'Failed to create user' },
607
- { status: 500 }
608
- );
609
- }
610
- }
611
- ```
612
-
613
- ### React Hook
677
+ ### Sign Up
614
678
 
615
679
  ```typescript
616
- // hooks/useWOWSQL.ts
617
- import { useState, useEffect } from 'react';
618
- import WOWSQLClient from '@wowsql/sdk';
619
-
620
- const client = new WOWSQLClient({
621
- projectUrl: process.env.NEXT_PUBLIC_WOWSQL_PROJECT!,
622
- apiKey: process.env.NEXT_PUBLIC_WOWSQL_API_KEY!
680
+ const { user, session } = await auth.signUp({
681
+ email: 'user@example.com',
682
+ password: 'SuperSecret123',
683
+ fullName: 'Demo User',
684
+ userMetadata: { referrer: 'landing-page', plan: 'free' }
623
685
  });
624
686
 
625
- export function useUsers() {
626
- const [users, setUsers] = useState([]);
627
- const [loading, setLoading] = useState(true);
628
- const [error, setError] = useState(null);
629
-
630
- useEffect(() => {
631
- async function fetchUsers() {
632
- try {
633
- const result = await client.table('users').get();
634
- setUsers(result.data);
635
- } catch (err) {
636
- setError(err);
637
- } finally {
638
- setLoading(false);
639
- }
640
- }
641
- fetchUsers();
642
- }, []);
643
-
644
- return { users, loading, error };
645
- }
687
+ console.log('User ID:', user?.id);
688
+ console.log('Email:', user?.email);
689
+ console.log('Access token:', session.accessToken);
646
690
  ```
647
691
 
648
- ### Express.js API
692
+ ### Sign In
649
693
 
650
694
  ```typescript
651
- // server.ts
652
- import express from 'express';
653
- import WOWSQLClient from '@wowsql/sdk';
654
-
655
- const app = express();
656
- const client = new WOWSQLClient({
657
- projectUrl: process.env.WOWSQL_PROJECT!,
658
- apiKey: process.env.WOWSQL_API_KEY!
659
- });
660
-
661
- app.use(express.json());
662
-
663
- // Get all posts
664
- app.get('/api/posts', async (req, res) => {
665
- try {
666
- const { page = 1, limit = 10 } = req.query;
667
- const posts = await client.table('posts')
668
- .order('created_at', 'desc')
669
- .limit(Number(limit))
670
- .offset((Number(page) - 1) * Number(limit))
671
- .get();
672
- res.json(posts);
673
- } catch (error) {
674
- res.status(500).json({ error: error.message });
675
- }
695
+ const { user, session } = await auth.signIn({
696
+ email: 'user@example.com',
697
+ password: 'SuperSecret123'
676
698
  });
677
699
 
678
- // Create post
679
- app.post('/api/posts', async (req, res) => {
680
- try {
681
- const result = await client.table('posts').create(req.body);
682
- res.status(201).json(result);
683
- } catch (error) {
684
- res.status(500).json({ error: error.message });
685
- }
700
+ // Persist the session for subsequent requests
701
+ auth.setSession({
702
+ accessToken: session.accessToken,
703
+ refreshToken: session.refreshToken
686
704
  });
687
705
 
688
- app.listen(3000, () => console.log('Server running on port 3000'));
706
+ console.log('Logged in as:', user?.email);
689
707
  ```
690
708
 
691
- ## Best Practices
709
+ ### Get Current User
692
710
 
693
- ### 1. Environment Variables
711
+ ```typescript
712
+ // Uses the stored session token automatically
713
+ const currentUser = await auth.getUser();
714
+ console.log(currentUser.id);
715
+ console.log(currentUser.email);
716
+ console.log(currentUser.email_verified);
717
+ console.log(currentUser.full_name);
718
+
719
+ // Or pass a specific access token
720
+ const user = await auth.getUser('specific-access-token');
721
+ ```
694
722
 
695
- Never hardcode API keys. Use environment variables:
723
+ ### OAuth Authentication
724
+
725
+ Complete OAuth flow with provider callback handling:
696
726
 
697
727
  ```typescript
698
- // .env
699
- WOWSQL_PROJECT=myproject
700
- WOWSQL_API_KEY=your-api-key
728
+ // Step 1: Get the authorization URL
729
+ const { authorizationUrl } = await auth.getOAuthAuthorizationUrl(
730
+ 'github',
731
+ 'https://app.your-domain.com/auth/callback'
732
+ );
733
+
734
+ // Redirect user to the authorization URL
735
+ window.location.href = authorizationUrl;
736
+
737
+ // Step 2: Handle the callback (in your callback route)
738
+ const urlParams = new URLSearchParams(window.location.search);
739
+ const code = urlParams.get('code');
740
+
741
+ const { user, session } = await auth.exchangeOAuthCallback(
742
+ 'github',
743
+ code!,
744
+ 'https://app.your-domain.com/auth/callback'
745
+ );
701
746
 
702
- // app.ts
703
- import WOWSQLClient from '@wowsql/sdk';
747
+ console.log('Logged in via GitHub:', user?.email);
748
+ console.log('Access token:', session.accessToken);
704
749
 
705
- const client = new WOWSQLClient({
706
- projectUrl: process.env.WOWSQL_PROJECT!,
707
- apiKey: process.env.WOWSQL_API_KEY!
750
+ // Save the session
751
+ auth.setSession({
752
+ accessToken: session.accessToken,
753
+ refreshToken: session.refreshToken
708
754
  });
709
755
  ```
710
756
 
711
- ### 2. Singleton Pattern
712
-
713
- Create a single client instance:
757
+ ### Password Reset
714
758
 
715
759
  ```typescript
716
- // lib/WOWSQL.ts
717
- import WOWSQLClient from '@wowsql/sdk';
718
-
719
- export const db = new WOWSQLClient({
720
- projectUrl: process.env.WOWSQL_PROJECT!,
721
- apiKey: process.env.WOWSQL_API_KEY!
722
- });
760
+ // Step 1: Request a password reset email
761
+ const result = await auth.forgotPassword('user@example.com');
762
+ console.log(result.message);
763
+ // "If that email exists, a password reset link has been sent"
723
764
 
724
- // Use in other files
725
- import { db } from './lib/WOWSQL';
726
- const users = await db.table('users').get();
765
+ // Step 2: Reset the password (user clicks link in email, you extract the token)
766
+ const resetResult = await auth.resetPassword(
767
+ 'reset-token-from-email',
768
+ 'NewSecurePassword123'
769
+ );
770
+ console.log(resetResult.message);
771
+ // "Password reset successfully! You can now login with your new password"
727
772
  ```
728
773
 
729
- ### 3. Type Definitions
730
-
731
- Define interfaces for your data:
774
+ ### OTP (One-Time Password)
732
775
 
733
776
  ```typescript
734
- // types/database.ts
735
- export interface User {
736
- id: number;
737
- name: string;
738
- email: string;
739
- created_at: string;
740
- }
777
+ // Send OTP to user's email
778
+ await auth.sendOtp('user@example.com', 'login');
779
+
780
+ // Verify OTP entered by user
781
+ const result = await auth.verifyOtp(
782
+ 'user@example.com',
783
+ '123456', // OTP code
784
+ 'login'
785
+ );
786
+ console.log('Verified:', result);
787
+
788
+ // OTP for password reset
789
+ await auth.sendOtp('user@example.com', 'password_reset');
790
+ const resetResult = await auth.verifyOtp(
791
+ 'user@example.com',
792
+ '654321',
793
+ 'password_reset',
794
+ 'NewPassword123' // new password
795
+ );
796
+ ```
741
797
 
742
- export interface Post {
743
- id: number;
744
- user_id: number;
745
- title: string;
746
- content: string;
747
- published_at: string | null;
748
- }
798
+ ### Magic Link
799
+
800
+ ```typescript
801
+ // Send a magic link to user's email
802
+ await auth.sendMagicLink('user@example.com', 'login');
749
803
 
750
- // Use in queries
751
- const users = await db.table<User>('users').get();
804
+ // The user clicks the link in their email, which redirects to your app
805
+ // with a token. No additional verification step needed on client side.
752
806
  ```
753
807
 
754
- ### 4. Error Handling
808
+ ### Email Verification
755
809
 
756
- Always wrap API calls in try-catch:
810
+ ```typescript
811
+ // Verify email with token (from verification email link)
812
+ await auth.verifyEmail('verification-token-from-email');
813
+
814
+ // Resend verification email
815
+ await auth.resendVerification('user@example.com');
816
+ ```
817
+
818
+ ### Change Password
757
819
 
758
820
  ```typescript
759
- import { WOWSQLError } from '@wowsql/sdk';
821
+ // Change password for authenticated user
822
+ await auth.changePassword(
823
+ 'currentPassword123',
824
+ 'newSecurePassword456'
825
+ );
760
826
 
761
- async function createUser(data: any) {
762
- try {
763
- const result = await db.table('users').create(data);
764
- return { success: true, data: result };
765
- } catch (error) {
766
- if (error instanceof WOWSQLError) {
767
- console.error(`Database error: ${error.message}`);
768
- return { success: false, error: error.message };
769
- }
770
- throw error;
771
- }
772
- }
827
+ // With explicit access token
828
+ await auth.changePassword(
829
+ 'currentPassword123',
830
+ 'newSecurePassword456',
831
+ 'specific-access-token'
832
+ );
773
833
  ```
774
834
 
775
- ## API Keys
835
+ ### Update User Profile
776
836
 
777
- WOWSQL uses **different API keys for different operations**. Understanding which key to use is crucial for proper authentication.
837
+ ```typescript
838
+ // Update user metadata
839
+ const updatedUser = await auth.updateUser({
840
+ full_name: 'New Display Name',
841
+ user_metadata: { theme: 'dark', language: 'en' }
842
+ });
778
843
 
779
- ### Key Types Overview
844
+ console.log('Updated name:', updatedUser.full_name);
780
845
 
781
- ## 🔑 Unified Authentication
846
+ // With explicit access token
847
+ const user = await auth.updateUser(
848
+ { full_name: 'Admin User' },
849
+ 'specific-access-token'
850
+ );
851
+ ```
782
852
 
783
- **✨ One Project = One Set of Keys for ALL Operations**
853
+ ### Session Management
784
854
 
785
- WOWSQL uses **unified authentication** - the same API keys work for both database operations AND authentication operations.
855
+ ```typescript
856
+ // Get current session
857
+ const session = auth.getSession();
858
+ if (session) {
859
+ console.log('Access token:', session.accessToken);
860
+ console.log('Refresh token:', session.refreshToken);
861
+ }
786
862
 
787
- | Operation Type | Recommended Key | Alternative Key | Used By |
788
- |---------------|----------------|-----------------|---------|
789
- | **Database Operations** (CRUD) | Service Role Key (`wowsql_service_...`) | Anonymous Key (`wowsql_anon_...`) | `WOWSQLClient` |
790
- | **Authentication Operations** (OAuth, sign-in) | Anonymous Key (`wowsql_anon_...`) | Service Role Key (`wowsql_service_...`) | `ProjectAuthClient` |
863
+ // Set session (e.g., restore from localStorage on page load)
864
+ auth.setSession({
865
+ accessToken: localStorage.getItem('access_token')!,
866
+ refreshToken: localStorage.getItem('refresh_token')!
867
+ });
791
868
 
792
- ### Where to Find Your Keys
869
+ // Refresh an expired session
870
+ const newSession = await auth.refreshSession();
871
+ // Or with explicit refresh token
872
+ const newSession = await auth.refreshSession('stored-refresh-token');
793
873
 
794
- All keys are found in: **WOWSQL Dashboard → Settings → API Keys** or **Authentication → PROJECT KEYS**
874
+ // Logout
875
+ await auth.logout();
876
+ // Or with explicit access token
877
+ await auth.logout('specific-access-token');
795
878
 
796
- 1. **Anonymous Key** (`wowsql_anon_...`) ✨ **Unified Key**
797
- - Location: "Anonymous Key (Public)"
798
- - Used for:
799
- - ✅ Client-side auth operations (signup, login, OAuth)
800
- - ✅ Public/client-side database operations with limited permissions
801
- - **Safe to expose** in frontend code (browser, mobile apps)
879
+ // Clear local session data
880
+ auth.clearSession();
881
+ ```
802
882
 
803
- 2. **Service Role Key** (`wowsql_service_...`) ✨ **Unified Key**
804
- - Location: "Service Role Key (keep secret)"
805
- - Used for:
806
- - ✅ Server-side auth operations (admin, full access)
807
- - ✅ Server-side database operations (full access, bypass RLS)
808
- - **NEVER expose** in frontend code - server-side only!
883
+ ## Storage Operations
809
884
 
810
- ### Database Operations
885
+ The `WowSQLStorage` client provides file storage capabilities backed by PostgreSQL.
811
886
 
812
- Use **Service Role Key** or **Anonymous Key** for database operations:
887
+ ### Initialization
813
888
 
814
889
  ```typescript
815
- import WOWSQLClient from '@wowsql/sdk';
816
-
817
- // Using Service Role Key (recommended for server-side, full access)
818
- const client = new WOWSQLClient({
819
- projectUrl: 'myproject',
820
- apiKey: 'wowsql_service_your-service-key-here' // Service Role Key
821
- });
890
+ import { WowSQLStorage } from 'wowsql';
822
891
 
823
- // Using Anonymous Key (for public/client-side access with limited permissions)
824
- const client = new WOWSQLClient({
825
- projectUrl: 'myproject',
826
- apiKey: 'wowsql_anon_your-anon-key-here' // Anonymous Key
892
+ const storage = new WowSQLStorage({
893
+ projectUrl: 'https://your-project.wowsql.com',
894
+ apiKey: 'your-api-key'
827
895
  });
828
-
829
- // Query data
830
- const users = await client.table('users').get();
831
896
  ```
832
897
 
833
- ### Authentication Operations
834
-
835
- **✨ UNIFIED AUTHENTICATION:** Use the **same keys** as database operations!
898
+ ### Bucket Operations
836
899
 
837
900
  ```typescript
838
- import { ProjectAuthClient } from '@wowsql/sdk';
901
+ // Create a new bucket
902
+ await storage.createBucket('avatars', {
903
+ public: true,
904
+ allowedMimeTypes: ['image/png', 'image/jpeg', 'image/webp'],
905
+ fileSizeLimit: 5 * 1024 * 1024 // 5 MB
906
+ });
839
907
 
840
- // Using Anonymous Key (recommended for client-side auth operations)
841
- const auth = new ProjectAuthClient({
842
- projectUrl: 'myproject',
843
- apiKey: 'wowsql_anon_your-anon-key-here' // Same key as database operations!
908
+ // List all buckets
909
+ const buckets = await storage.listBuckets();
910
+ buckets.forEach(bucket => {
911
+ console.log(`${bucket.name}: ${bucket.public ? 'public' : 'private'}`);
844
912
  });
845
913
 
846
- // Using Service Role Key (for server-side auth operations)
847
- const auth = new ProjectAuthClient({
848
- projectUrl: 'myproject',
849
- apiKey: 'wowsql_service_your-service-key-here' // Same key as database operations!
914
+ // Get bucket details
915
+ const bucket = await storage.getBucket('avatars');
916
+ console.log(bucket.name, bucket.created_at);
917
+
918
+ // Update bucket settings
919
+ await storage.updateBucket('avatars', {
920
+ public: false,
921
+ fileSizeLimit: 10 * 1024 * 1024 // increase to 10 MB
850
922
  });
851
923
 
852
- // OAuth authentication
853
- const { authorizationUrl } = await auth.getOAuthAuthorizationUrl(
854
- 'github',
855
- 'https://app.example.com/auth/callback'
856
- );
924
+ // Delete a bucket
925
+ await storage.deleteBucket('old-bucket');
857
926
  ```
858
927
 
859
- **Note:** The `publicApiKey` parameter is deprecated but still works for backward compatibility. Use `apiKey` instead.
928
+ ### File Upload
860
929
 
861
- ### Environment Variables
930
+ ```typescript
931
+ // Upload file data (Buffer, Blob, or File)
932
+ const result = await storage.upload('documents', fileData, 'reports/q1.pdf');
933
+ console.log('Uploaded:', result);
934
+
935
+ // Using the uploadFile alias
936
+ await storage.uploadFile('documents', fileBlob, 'reports/', 'q1-report.pdf');
937
+
938
+ // Upload from a local file path (Node.js)
939
+ await storage.uploadFromPath(
940
+ '/home/user/report.pdf',
941
+ 'documents',
942
+ 'reports/'
943
+ );
944
+ ```
862
945
 
863
- Best practice: Use environment variables for API keys:
946
+ ### File Download
864
947
 
865
948
  ```typescript
866
- // UNIFIED AUTHENTICATION: Same keys for both operations!
949
+ // Download file to memory
950
+ const data = await storage.download('documents', 'reports/q1.pdf');
951
+
952
+ // Download to a local file path (Node.js)
953
+ await storage.downloadToFile(
954
+ 'documents',
955
+ 'reports/q1.pdf',
956
+ '/home/user/downloads/q1.pdf'
957
+ );
958
+ ```
867
959
 
868
- // Database operations - Service Role Key
869
- const dbClient = new WOWSQLClient({
870
- projectUrl: process.env.WOWSQL_PROJECT_URL!,
871
- apiKey: process.env.WOWSQL_SERVICE_ROLE_KEY! // or WOWSQL_ANON_KEY
960
+ ### File Listing
961
+
962
+ ```typescript
963
+ // List all files in a bucket
964
+ const files = await storage.listFiles('documents');
965
+ files.forEach(file => {
966
+ console.log(`${file.name}: ${file.size} bytes`);
872
967
  });
873
968
 
874
- // Authentication operations - Use the SAME key!
875
- const authClient = new ProjectAuthClient({
876
- projectUrl: process.env.WOWSQL_PROJECT_URL!,
877
- apiKey: process.env.WOWSQL_ANON_KEY! // Same key for client-side auth
878
- // Or use WOWSQL_SERVICE_ROLE_KEY for server-side auth
969
+ // List with options (prefix, limit, offset)
970
+ const files = await storage.listFiles('documents', {
971
+ prefix: 'reports/',
972
+ limit: 50,
973
+ offset: 0
879
974
  });
880
975
  ```
881
976
 
882
- ### Key Usage Summary
977
+ ### File Deletion and URLs
883
978
 
884
- **✨ UNIFIED AUTHENTICATION:**
885
- - **`WOWSQLClient`** Uses **Service Role Key** or **Anonymous Key** for database operations
886
- - **`ProjectAuthClient`** → Uses **Anonymous Key** (client-side) or **Service Role Key** (server-side) for authentication operations
887
- - **Same keys work for both** database AND authentication operations! 🎉
888
- - **Anonymous Key** (`wowsql_anon_...`) → Client-side operations (auth + database)
889
- - **Service Role Key** (`wowsql_service_...`) → Server-side operations (auth + database)
979
+ ```typescript
980
+ // Delete a file
981
+ await storage.deleteFile('documents', 'reports/old-report.pdf');
890
982
 
891
- ### Security Best Practices
983
+ // Get a public URL for a file
984
+ const url = storage.getPublicUrl('avatars', 'users/123/avatar.png');
985
+ console.log(url); // https://your-project.wowsql.com/storage/v1/...
986
+ ```
892
987
 
893
- 1. **Never expose Service Role Key** in client-side code or public repositories
894
- 2. **Use Anonymous Key** for client-side authentication flows (same key as database operations)
895
- 3. **Use Anonymous Key** for public database access with limited permissions
896
- 4. **Store keys in environment variables**, never hardcode them
897
- 5. **Rotate keys regularly** if compromised
988
+ ### Storage Stats and Quota
898
989
 
899
- ### Troubleshooting
990
+ ```typescript
991
+ // Get storage statistics
992
+ const stats = await storage.getStats();
993
+ console.log('Total files:', stats.total_files);
994
+ console.log('Total size:', stats.total_size);
995
+
996
+ // Check storage quota
997
+ const quota = await storage.getQuota();
998
+ console.log(`Used: ${quota.used_gb} GB`);
999
+ console.log(`Total: ${quota.total_gb} GB`);
1000
+ console.log(`Available: ${quota.available_gb} GB`);
1001
+
1002
+ // Check quota before uploading
1003
+ if (quota.available_gb > fileSizeInGb) {
1004
+ await storage.upload('documents', fileData, 'path/to/file.pdf');
1005
+ } else {
1006
+ console.log('Storage limit reached. Upgrade your plan.');
1007
+ }
1008
+ ```
900
1009
 
901
- **Error: "Invalid API key for project"**
902
- - Ensure you're using the correct key type for the operation
903
- - Database operations require Service Role Key or Anonymous Key
904
- - Authentication operations require Anonymous Key (client-side) or Service Role Key (server-side)
905
- - Verify the key is copied correctly (no extra spaces)
1010
+ ### Storage Error Handling
906
1011
 
907
- **Error: "Authentication failed"**
908
- - Check that you're using the correct key: Anonymous Key for client-side, Service Role Key for server-side
909
- - Verify the project URL matches your dashboard
910
- - Ensure the key hasn't been revoked or expired
1012
+ ```typescript
1013
+ import { WowSQLStorage, StorageError, StorageLimitExceededError } from 'wowsql';
1014
+
1015
+ try {
1016
+ await storage.upload('documents', largeFile, 'path/to/huge-file.zip');
1017
+ } catch (error) {
1018
+ if (error instanceof StorageLimitExceededError) {
1019
+ console.error('Storage limit exceeded:', error.message);
1020
+ console.error('Please upgrade your plan or delete old files');
1021
+ } else if (error instanceof StorageError) {
1022
+ console.error('Storage error:', error.message);
1023
+ }
1024
+ }
1025
+ ```
911
1026
 
912
- ## 🔧 Schema Management
1027
+ ## Schema Management
913
1028
 
914
- Programmatically manage your database schema with the `WOWSQLSchema` client.
1029
+ Programmatically manage your database schema with the `WowSQLSchema` client.
915
1030
 
916
- > **⚠️ IMPORTANT**: Schema operations require a **Service Role Key** (`service_*`). Anonymous keys will return a 403 Forbidden error.
1031
+ > **IMPORTANT**: Schema operations require a **Service Role Key** (`wowsql_service_...`). Anonymous keys will return a 403 Forbidden error.
917
1032
 
918
- ### Quick Start
1033
+ ### Initialization
919
1034
 
920
1035
  ```typescript
921
- import { WOWSQLSchema } from '@wowsql/sdk';
1036
+ import { WowSQLSchema } from 'wowsql';
922
1037
 
923
- // Initialize schema client with SERVICE ROLE KEY
924
- const schema = new WOWSQLSchema(
925
- 'https://your-project.wowsql.com',
926
- 'service_xyz789...' // ⚠️ Backend only! Never expose!
927
- );
1038
+ const schema = new WowSQLSchema({
1039
+ projectUrl: 'https://your-project.wowsql.com',
1040
+ serviceKey: 'wowsql_service_xyz789...'
1041
+ });
928
1042
  ```
929
1043
 
930
1044
  ### Create Table
931
1045
 
932
1046
  ```typescript
933
- // Create a new table
934
1047
  await schema.createTable({
935
1048
  tableName: 'products',
936
1049
  columns: [
@@ -946,8 +1059,6 @@ await schema.createTable({
946
1059
  { name: 'idx_price', columns: ['price'] }
947
1060
  ]
948
1061
  });
949
-
950
- console.log('Table created successfully!');
951
1062
  ```
952
1063
 
953
1064
  ### Alter Table
@@ -965,7 +1076,7 @@ await schema.alterTable({
965
1076
  await schema.alterTable({
966
1077
  tableName: 'products',
967
1078
  modifyColumns: [
968
- { name: 'price', type: 'DECIMAL(12,2)' } // Increase precision
1079
+ { name: 'price', type: 'DECIMAL(12,2)' }
969
1080
  ]
970
1081
  });
971
1082
 
@@ -984,6 +1095,37 @@ await schema.alterTable({
984
1095
  });
985
1096
  ```
986
1097
 
1098
+ ### Column Operations
1099
+
1100
+ ```typescript
1101
+ // Add a column
1102
+ await schema.addColumn('products', 'description', 'TEXT', {
1103
+ not_null: false,
1104
+ default: "''"
1105
+ });
1106
+
1107
+ // Drop a column
1108
+ await schema.dropColumn('products', 'old_field');
1109
+
1110
+ // Rename a column
1111
+ await schema.renameColumn('products', 'name', 'product_name');
1112
+
1113
+ // Modify a column type or constraints
1114
+ await schema.modifyColumn('products', 'price', 'DECIMAL(14,2)', {
1115
+ not_null: true
1116
+ });
1117
+ ```
1118
+
1119
+ ### Index Operations
1120
+
1121
+ ```typescript
1122
+ // Create an index
1123
+ await schema.createIndex('products', ['category', 'price'], {
1124
+ name: 'idx_category_price',
1125
+ unique: false
1126
+ });
1127
+ ```
1128
+
987
1129
  ### Drop Table
988
1130
 
989
1131
  ```typescript
@@ -991,127 +1133,581 @@ await schema.alterTable({
991
1133
  await schema.dropTable('old_table');
992
1134
 
993
1135
  // Drop with CASCADE (removes dependent objects)
994
- await schema.dropTable('products', { cascade: true });
1136
+ await schema.dropTable('products', true);
995
1137
  ```
996
1138
 
997
1139
  ### Execute Raw SQL
998
1140
 
999
1141
  ```typescript
1000
- // Execute custom schema SQL
1001
1142
  await schema.executeSQL(`
1002
- CREATE INDEX idx_product_name
1143
+ CREATE INDEX idx_product_name
1003
1144
  ON products(product_name);
1004
1145
  `);
1005
1146
 
1006
- // Add a foreign key constraint
1007
1147
  await schema.executeSQL(`
1008
- ALTER TABLE orders
1009
- ADD CONSTRAINT fk_product
1010
- FOREIGN KEY (product_id)
1148
+ ALTER TABLE orders
1149
+ ADD CONSTRAINT fk_product
1150
+ FOREIGN KEY (product_id)
1011
1151
  REFERENCES products(id);
1012
1152
  `);
1013
1153
  ```
1014
1154
 
1015
- ### Security & Best Practices
1155
+ ### Schema Introspection
1156
+
1157
+ ```typescript
1158
+ // List all tables
1159
+ const tables = await schema.listTables();
1160
+ console.log(tables);
1161
+
1162
+ // Get detailed table schema
1163
+ const tableSchema = await schema.getTableSchema('users');
1164
+ console.log(tableSchema.columns);
1165
+ console.log(tableSchema.primary_key);
1166
+ console.log(tableSchema.indexes);
1167
+ ```
1168
+
1169
+ ### Schema Error Handling
1170
+
1171
+ ```typescript
1172
+ import { WowSQLSchema, SchemaPermissionError } from 'wowsql';
1016
1173
 
1017
- #### ✅ DO:
1174
+ try {
1175
+ await schema.createTable({
1176
+ tableName: 'test',
1177
+ columns: [{ name: 'id', type: 'INT', auto_increment: true }],
1178
+ primaryKey: 'id'
1179
+ });
1180
+ } catch (error) {
1181
+ if (error instanceof SchemaPermissionError) {
1182
+ console.error('Permission denied:', error.message);
1183
+ console.error('Make sure you are using a SERVICE ROLE KEY!');
1184
+ } else {
1185
+ console.error('Error:', error);
1186
+ }
1187
+ }
1188
+ ```
1189
+
1190
+ ### Schema Security Best Practices
1191
+
1192
+ **DO:**
1018
1193
  - Use service role keys **only in backend/server code** (Node.js, Next.js API routes)
1019
1194
  - Store service keys in environment variables
1020
1195
  - Use anonymous keys for client-side data operations
1021
1196
  - Test schema changes in development first
1022
1197
 
1023
- #### ❌ DON'T:
1198
+ **DON'T:**
1024
1199
  - Never expose service role keys in frontend/browser code
1025
1200
  - Never commit service keys to version control
1026
- - Don't use anonymous keys for schema operations (will fail)
1201
+ - Don't use anonymous keys for schema operations (will fail with 403)
1202
+
1203
+ ## API Keys
1204
+
1205
+ WowSQL uses **unified authentication** - the same API keys work for database operations, authentication, storage, and schema management.
1206
+
1207
+ ### Key Types
1208
+
1209
+ | Operation Type | Recommended Key | Alternative Key | Used By |
1210
+ |---|---|---|---|
1211
+ | **Database Operations** (CRUD) | Service Role Key (`wowsql_service_...`) | Anonymous Key (`wowsql_anon_...`) | `WowSQLClient` |
1212
+ | **Authentication** (sign-up, login, OAuth) | Anonymous Key (`wowsql_anon_...`) | Service Role Key (`wowsql_service_...`) | `ProjectAuthClient` |
1213
+ | **Storage** (upload, download) | Service Role Key (`wowsql_service_...`) | Anonymous Key (`wowsql_anon_...`) | `WowSQLStorage` |
1214
+ | **Schema Management** (DDL) | Service Role Key (`wowsql_service_...`) | N/A | `WowSQLSchema` |
1215
+
1216
+ ### Where to Find Your Keys
1217
+
1218
+ All keys are in: **WowSQL Dashboard > Settings > API Keys** or **Authentication > PROJECT KEYS**
1219
+
1220
+ 1. **Anonymous Key** (`wowsql_anon_...`)
1221
+ - Client-side auth operations (signup, login, OAuth)
1222
+ - Public/client-side database operations with limited permissions
1223
+ - **Safe to expose** in frontend code (browser, mobile apps)
1224
+
1225
+ 2. **Service Role Key** (`wowsql_service_...`)
1226
+ - Server-side operations with full access (bypass RLS)
1227
+ - Schema management operations
1228
+ - **NEVER expose** in frontend code - server-side only!
1229
+
1230
+ ### Environment Variables
1231
+
1232
+ ```bash
1233
+ # .env
1234
+ WOWSQL_PROJECT_URL=https://your-project.wowsql.com
1235
+ WOWSQL_ANON_KEY=wowsql_anon_your-anon-key
1236
+ WOWSQL_SERVICE_ROLE_KEY=wowsql_service_your-service-key
1237
+ ```
1238
+
1239
+ ```typescript
1240
+ const dbClient = new WowSQLClient({
1241
+ projectUrl: process.env.WOWSQL_PROJECT_URL!,
1242
+ apiKey: process.env.WOWSQL_SERVICE_ROLE_KEY!
1243
+ });
1027
1244
 
1028
- ### Example: Next.js API Route Migration
1245
+ const authClient = new ProjectAuthClient({
1246
+ projectUrl: process.env.WOWSQL_PROJECT_URL!,
1247
+ apiKey: process.env.WOWSQL_ANON_KEY!
1248
+ });
1249
+
1250
+ const storage = new WowSQLStorage({
1251
+ projectUrl: process.env.WOWSQL_PROJECT_URL!,
1252
+ apiKey: process.env.WOWSQL_SERVICE_ROLE_KEY!
1253
+ });
1254
+
1255
+ const schema = new WowSQLSchema({
1256
+ projectUrl: process.env.WOWSQL_PROJECT_URL!,
1257
+ serviceKey: process.env.WOWSQL_SERVICE_ROLE_KEY!
1258
+ });
1259
+ ```
1260
+
1261
+ ### Security Best Practices
1262
+
1263
+ 1. **Never expose Service Role Key** in client-side code or public repositories
1264
+ 2. **Use Anonymous Key** for client-side authentication and limited database access
1265
+ 3. **Store keys in environment variables**, never hardcode them
1266
+ 4. **Rotate keys regularly** if compromised
1267
+
1268
+ ### Troubleshooting
1269
+
1270
+ **Error: "Invalid API key for project"**
1271
+ - Ensure you're using the correct key type for the operation
1272
+ - Verify the key is copied correctly (no extra spaces)
1273
+ - Check that the project URL matches your dashboard
1274
+
1275
+ **Error: "Authentication failed"**
1276
+ - Check that you're using the correct key: Anonymous Key for client-side, Service Role Key for server-side
1277
+ - Ensure the key hasn't been revoked or expired
1278
+
1279
+ ## Error Handling
1280
+
1281
+ ```typescript
1282
+ import {
1283
+ WowSQLClient,
1284
+ WowSQLStorage,
1285
+ WOWSQLError,
1286
+ StorageError,
1287
+ StorageLimitExceededError,
1288
+ SchemaPermissionError
1289
+ } from 'wowsql';
1290
+
1291
+ // Database errors
1292
+ try {
1293
+ const users = await client.table('users').select('*').get();
1294
+ } catch (error) {
1295
+ if (error instanceof WOWSQLError) {
1296
+ console.error(`Database error [${error.statusCode}]: ${error.message}`);
1297
+ console.error('Response:', error.response);
1298
+ }
1299
+ }
1300
+
1301
+ // Storage errors
1302
+ try {
1303
+ await storage.upload('bucket', fileData, 'path/to/file');
1304
+ } catch (error) {
1305
+ if (error instanceof StorageLimitExceededError) {
1306
+ console.error('Storage full:', error.message);
1307
+ } else if (error instanceof StorageError) {
1308
+ console.error('Storage error:', error.message);
1309
+ }
1310
+ }
1311
+
1312
+ // Schema errors
1313
+ try {
1314
+ await schema.createTable({ tableName: 'test', columns: [] });
1315
+ } catch (error) {
1316
+ if (error instanceof SchemaPermissionError) {
1317
+ console.error('Use a Service Role Key for schema operations');
1318
+ }
1319
+ }
1320
+
1321
+ // Generic catch-all
1322
+ try {
1323
+ await client.table('users').getById(999);
1324
+ } catch (error) {
1325
+ if (error instanceof WOWSQLError) {
1326
+ switch (error.statusCode) {
1327
+ case 401: console.error('Unauthorized - check your API key'); break;
1328
+ case 404: console.error('Record not found'); break;
1329
+ case 429: console.error('Rate limit exceeded - slow down'); break;
1330
+ default: console.error(`Error ${error.statusCode}: ${error.message}`);
1331
+ }
1332
+ } else {
1333
+ console.error('Unexpected error:', error);
1334
+ }
1335
+ }
1336
+ ```
1337
+
1338
+ ## Configuration
1339
+
1340
+ ### Basic Configuration
1341
+
1342
+ ```typescript
1343
+ const client = new WowSQLClient({
1344
+ projectUrl: 'https://your-project.wowsql.com',
1345
+ apiKey: 'your-api-key'
1346
+ });
1347
+ ```
1348
+
1349
+ ### Advanced Configuration
1350
+
1351
+ ```typescript
1352
+ const client = new WowSQLClient({
1353
+ projectUrl: 'your-project', // subdomain or full URL
1354
+ apiKey: 'your-api-key',
1355
+ baseDomain: 'wowsql.com', // custom domain (default: wowsql.com)
1356
+ secure: true, // use HTTPS (default: true)
1357
+ timeout: 30000, // request timeout in ms (default: 30000)
1358
+ verifySsl: true // verify SSL certificates (default: true)
1359
+ });
1360
+ ```
1361
+
1362
+ ### Storage Timeout
1363
+
1364
+ ```typescript
1365
+ const storage = new WowSQLStorage({
1366
+ projectUrl: 'https://your-project.wowsql.com',
1367
+ apiKey: 'your-api-key',
1368
+ timeout: 120000 // 2 minutes for large file uploads
1369
+ });
1370
+ ```
1371
+
1372
+ ### Auth with Custom Storage
1373
+
1374
+ ```typescript
1375
+ const auth = new ProjectAuthClient({
1376
+ projectUrl: 'https://your-project.wowsql.com',
1377
+ apiKey: 'your-anon-key',
1378
+ storage: {
1379
+ getItem: (key) => localStorage.getItem(key),
1380
+ setItem: (key, value) => localStorage.setItem(key, value),
1381
+ removeItem: (key) => localStorage.removeItem(key)
1382
+ }
1383
+ });
1384
+ ```
1385
+
1386
+ ### Disabling SSL Verification (development only)
1387
+
1388
+ ```typescript
1389
+ const client = new WowSQLClient({
1390
+ projectUrl: 'https://localhost:8443',
1391
+ apiKey: 'dev-key',
1392
+ verifySsl: false
1393
+ });
1394
+ ```
1395
+
1396
+ ## TypeScript Support
1397
+
1398
+ The SDK is written in TypeScript and provides full type safety with generics.
1399
+
1400
+ ### Defining Your Types
1401
+
1402
+ ```typescript
1403
+ interface User {
1404
+ id: number;
1405
+ name: string;
1406
+ email: string;
1407
+ age: number;
1408
+ created_at: string;
1409
+ }
1410
+
1411
+ interface Post {
1412
+ id: number;
1413
+ user_id: number;
1414
+ title: string;
1415
+ content: string;
1416
+ published: boolean;
1417
+ published_at: string | null;
1418
+ }
1419
+
1420
+ // Type-safe queries
1421
+ const users = await client.table<User>('users').get();
1422
+ users.data.forEach(user => {
1423
+ console.log(user.name); // TypeScript knows this is string
1424
+ console.log(user.age); // TypeScript knows this is number
1425
+ });
1426
+
1427
+ // Type-safe getById
1428
+ const user: User = await client.table<User>('users').getById(1);
1429
+
1430
+ // Type-safe create
1431
+ await client.table<User>('users').create({
1432
+ name: 'John',
1433
+ email: 'john@example.com',
1434
+ age: 30
1435
+ });
1436
+
1437
+ // Type-safe query builder
1438
+ const results = await client.table<User>('users')
1439
+ .select('id', 'name', 'email')
1440
+ .eq('age', 30)
1441
+ .orderBy('name', 'asc')
1442
+ .limit(10)
1443
+ .get();
1444
+ ```
1445
+
1446
+ ### Key Interfaces
1447
+
1448
+ ```typescript
1449
+ import type {
1450
+ WowSQLConfig,
1451
+ QueryOptions,
1452
+ FilterExpression,
1453
+ HavingFilter,
1454
+ OrderByItem,
1455
+ QueryResponse,
1456
+ CreateResponse,
1457
+ UpdateResponse,
1458
+ DeleteResponse,
1459
+ TableSchema,
1460
+ ColumnInfo,
1461
+ AuthTokenStorage,
1462
+ AuthUser,
1463
+ AuthSession,
1464
+ AuthResponse,
1465
+ StorageBucket,
1466
+ StorageFile,
1467
+ StorageQuota
1468
+ } from 'wowsql';
1469
+ ```
1470
+
1471
+ ## Examples
1472
+
1473
+ ### Blog Application
1474
+
1475
+ ```typescript
1476
+ import { WowSQLClient } from 'wowsql';
1477
+
1478
+ interface Post {
1479
+ id: number;
1480
+ title: string;
1481
+ content: string;
1482
+ author_id: number;
1483
+ published: boolean;
1484
+ created_at: string;
1485
+ }
1486
+
1487
+ interface Comment {
1488
+ id: number;
1489
+ post_id: number;
1490
+ author_name: string;
1491
+ body: string;
1492
+ created_at: string;
1493
+ }
1494
+
1495
+ const client = new WowSQLClient({
1496
+ projectUrl: process.env.WOWSQL_PROJECT_URL!,
1497
+ apiKey: process.env.WOWSQL_SERVICE_ROLE_KEY!
1498
+ });
1499
+
1500
+ // Create a new post
1501
+ const post = await client.table<Post>('posts').create({
1502
+ title: 'Hello World',
1503
+ content: 'My first blog post',
1504
+ author_id: 1,
1505
+ published: true
1506
+ });
1507
+
1508
+ // Get published posts with pagination
1509
+ const posts = await client.table<Post>('posts')
1510
+ .select('id', 'title', 'content', 'created_at')
1511
+ .eq('published', true)
1512
+ .orderBy('created_at', 'desc')
1513
+ .limit(10)
1514
+ .get();
1515
+
1516
+ // Get a single post by ID
1517
+ const singlePost = await client.table<Post>('posts').getById(1);
1518
+
1519
+ // Get comments for a post
1520
+ const comments = await client.table<Comment>('comments')
1521
+ .select('*')
1522
+ .eq('post_id', 1)
1523
+ .orderBy('created_at', 'asc')
1524
+ .get();
1525
+
1526
+ // Get post statistics
1527
+ const stats = await client.table<Post>('posts')
1528
+ .select('DATE(created_at) as date', 'COUNT(*) as count')
1529
+ .eq('published', true)
1530
+ .groupBy('DATE(created_at)')
1531
+ .orderBy('date', 'desc')
1532
+ .limit(30)
1533
+ .get();
1534
+ ```
1535
+
1536
+ ### File Upload Application
1029
1537
 
1030
1538
  ```typescript
1031
- // app/api/migrate/route.ts
1032
- import { WOWSQLSchema } from '@wowsql/sdk';
1539
+ import { WowSQLClient, WowSQLStorage } from 'wowsql';
1540
+
1541
+ const client = new WowSQLClient({
1542
+ projectUrl: process.env.WOWSQL_PROJECT_URL!,
1543
+ apiKey: process.env.WOWSQL_SERVICE_ROLE_KEY!
1544
+ });
1545
+
1546
+ const storage = new WowSQLStorage({
1547
+ projectUrl: process.env.WOWSQL_PROJECT_URL!,
1548
+ apiKey: process.env.WOWSQL_SERVICE_ROLE_KEY!
1549
+ });
1550
+
1551
+ // Create an avatars bucket
1552
+ await storage.createBucket('avatars', { public: true });
1553
+
1554
+ // Upload user avatar
1555
+ const userId = 123;
1556
+ const avatarPath = `users/${userId}/avatar.jpg`;
1557
+ await storage.upload('avatars', avatarBuffer, avatarPath);
1558
+
1559
+ // Get the public URL and save to database
1560
+ const avatarUrl = storage.getPublicUrl('avatars', avatarPath);
1561
+ await client.table('users').update(userId, {
1562
+ avatar_url: avatarUrl
1563
+ });
1564
+
1565
+ // List user's uploaded files
1566
+ const files = await storage.listFiles('avatars', {
1567
+ prefix: `users/${userId}/`
1568
+ });
1569
+ console.log(`User has ${files.length} files`);
1570
+
1571
+ // Check storage quota before upload
1572
+ const quota = await storage.getQuota();
1573
+ if (quota.available_gb > 0.1) {
1574
+ await storage.upload('avatars', largeFile, 'users/123/banner.jpg');
1575
+ } else {
1576
+ console.log('Low storage! Consider upgrading.');
1577
+ }
1578
+ ```
1579
+
1580
+ ### Next.js API Route
1581
+
1582
+ ```typescript
1583
+ // app/api/users/route.ts
1033
1584
  import { NextResponse } from 'next/server';
1585
+ import { WowSQLClient } from 'wowsql';
1586
+
1587
+ const client = new WowSQLClient({
1588
+ projectUrl: process.env.WOWSQL_PROJECT_URL!,
1589
+ apiKey: process.env.WOWSQL_SERVICE_ROLE_KEY!
1590
+ });
1591
+
1592
+ export async function GET(request: Request) {
1593
+ const { searchParams } = new URL(request.url);
1594
+ const page = parseInt(searchParams.get('page') || '1');
1595
+ const perPage = 20;
1034
1596
 
1035
- export async function POST() {
1036
- const schema = new WOWSQLSchema(
1037
- process.env.WOWSQL_PROJECT_URL!,
1038
- process.env.WOWSQL_SERVICE_KEY! // From env var
1039
- );
1040
-
1041
1597
  try {
1042
- // Create users table
1043
- await schema.createTable({
1044
- tableName: 'users',
1045
- columns: [
1046
- { name: 'id', type: 'INT', auto_increment: true },
1047
- { name: 'email', type: 'VARCHAR(255)', unique: true, not_null: true },
1048
- { name: 'name', type: 'VARCHAR(255)', not_null: true },
1049
- { name: 'created_at', type: 'TIMESTAMP', default: 'CURRENT_TIMESTAMP' }
1050
- ],
1051
- primaryKey: 'id',
1052
- indexes: [{ name: 'idx_email', columns: ['email'] }]
1053
- });
1054
-
1055
- return NextResponse.json({ success: true });
1598
+ const result = await client.table('users')
1599
+ .select('id', 'name', 'email', 'created_at')
1600
+ .orderBy('created_at', 'desc')
1601
+ .paginate(page, perPage);
1602
+
1603
+ return NextResponse.json(result);
1056
1604
  } catch (error) {
1057
- return NextResponse.json({ error: error.message }, { status: 500 });
1605
+ return NextResponse.json({ error: 'Failed to fetch users' }, { status: 500 });
1606
+ }
1607
+ }
1608
+
1609
+ export async function POST(request: Request) {
1610
+ try {
1611
+ const body = await request.json();
1612
+ const result = await client.table('users').create(body);
1613
+ return NextResponse.json(result, { status: 201 });
1614
+ } catch (error) {
1615
+ return NextResponse.json({ error: 'Failed to create user' }, { status: 500 });
1058
1616
  }
1059
1617
  }
1060
1618
  ```
1061
1619
 
1062
- ### Error Handling
1620
+ ### Migration Script
1063
1621
 
1064
1622
  ```typescript
1065
- import { WOWSQLSchema, PermissionError } from '@wowsql/sdk';
1623
+ // scripts/migrate.ts
1624
+ import { WowSQLSchema } from 'wowsql';
1066
1625
 
1067
- try {
1068
- const schema = new WOWSQLSchema(
1069
- 'https://your-project.wowsql.com',
1070
- 'service_xyz...'
1071
- );
1072
-
1626
+ async function runMigration() {
1627
+ const schema = new WowSQLSchema({
1628
+ projectUrl: process.env.WOWSQL_PROJECT_URL!,
1629
+ serviceKey: process.env.WOWSQL_SERVICE_ROLE_KEY!
1630
+ });
1631
+
1632
+ // Create users table
1073
1633
  await schema.createTable({
1074
- tableName: 'test',
1075
- columns: [{ name: 'id', type: 'INT' }]
1634
+ tableName: 'users',
1635
+ columns: [
1636
+ { name: 'id', type: 'INT', auto_increment: true },
1637
+ { name: 'email', type: 'VARCHAR(255)', unique: true, not_null: true },
1638
+ { name: 'name', type: 'VARCHAR(255)', not_null: true },
1639
+ { name: 'created_at', type: 'TIMESTAMP', default: 'CURRENT_TIMESTAMP' }
1640
+ ],
1641
+ primaryKey: 'id',
1642
+ indexes: [{ name: 'idx_email', columns: ['email'] }]
1076
1643
  });
1077
- } catch (error) {
1078
- if (error instanceof PermissionError) {
1079
- console.error('Permission denied:', error.message);
1080
- console.error('Make sure you\'re using a SERVICE ROLE KEY!');
1081
- } else {
1082
- console.error('Error:', error);
1083
- }
1644
+
1645
+ // Create posts table
1646
+ await schema.createTable({
1647
+ tableName: 'posts',
1648
+ columns: [
1649
+ { name: 'id', type: 'INT', auto_increment: true },
1650
+ { name: 'user_id', type: 'INT', not_null: true },
1651
+ { name: 'title', type: 'VARCHAR(255)', not_null: true },
1652
+ { name: 'content', type: 'TEXT' },
1653
+ { name: 'published', type: 'BOOLEAN', default: 'false' },
1654
+ { name: 'created_at', type: 'TIMESTAMP', default: 'CURRENT_TIMESTAMP' }
1655
+ ],
1656
+ primaryKey: 'id'
1657
+ });
1658
+
1659
+ // Add foreign key
1660
+ await schema.executeSQL(`
1661
+ ALTER TABLE posts
1662
+ ADD CONSTRAINT fk_user
1663
+ FOREIGN KEY (user_id)
1664
+ REFERENCES users(id);
1665
+ `);
1666
+
1667
+ console.log('Migration completed!');
1084
1668
  }
1669
+
1670
+ runMigration().catch(console.error);
1085
1671
  ```
1086
1672
 
1087
- ---
1673
+ ## Requirements
1674
+
1675
+ - Node.js 16+
1676
+ - TypeScript 4.7+ (for TypeScript users)
1677
+ - Works in Node.js, browser, Deno, Bun, and edge runtimes
1088
1678
 
1089
1679
  ## FAQ
1090
1680
 
1091
1681
  ### Can I use this in the browser?
1092
1682
 
1093
- 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 **Anonymous Key** for both authentication and limited database access. For full database operations, use a backend proxy with Service Role Key.
1683
+ Yes! The SDK works in both Node.js and browser environments. Use the **Anonymous Key** for client-side code. Never expose the Service Role Key in frontend code.
1094
1684
 
1095
1685
  ### What about rate limits?
1096
1686
 
1097
- Rate limits depend on your WOWSQL plan. The SDK will throw a `WOWSQLError` with status code 429 when rate limits are exceeded.
1687
+ Rate limits depend on your WowSQL plan. The SDK throws a `WOWSQLError` with status code 429 when rate limits are exceeded.
1098
1688
 
1099
1689
  ### Does it support transactions?
1100
1690
 
1101
- Currently, the SDK doesn't support transactions directly. Use raw SQL queries for complex transactional operations.
1691
+ Currently, the SDK doesn't support transactions directly. Use raw SQL queries via `client.query()` for complex transactional operations.
1102
1692
 
1103
1693
  ### How do I upgrade?
1104
1694
 
1105
1695
  ```bash
1106
- npm update @wowsql/sdk
1696
+ npm update wowsql
1107
1697
  ```
1108
1698
 
1699
+ ## Links
1700
+
1701
+ - [Documentation](https://wowsql.com/docs)
1702
+ - [Website](https://wowsql.com)
1703
+ - [Discord](https://discord.gg/wowsql)
1704
+ - [GitHub Issues](https://github.com/wowsql/wowsql/issues)
1705
+
1109
1706
  ## Support
1110
1707
 
1111
- - 📧 Email: support@wowsql.com
1112
- - 💬 Discord: [Join our community](https://discord.gg/WOWSQL)
1113
- - 📚 Documentation: [docs.wowsql.com](https://docs.wowsql.com)
1114
- - 🐛 Issues: [GitHub Issues](https://github.com/wowsql/wowsql/issues)
1708
+ - Email: support@wowsql.com
1709
+ - Discord: https://discord.gg/wowsql
1710
+ - Documentation: https://wowsql.com/docs
1115
1711
 
1116
1712
  ## Contributing
1117
1713
 
@@ -1127,6 +1723,4 @@ See [CHANGELOG.md](CHANGELOG.md) for version history.
1127
1723
 
1128
1724
  ---
1129
1725
 
1130
- Made with ❤️ by the WOWSQL Team
1131
-
1132
-
1726
+ Made with care by the WowSQL Team