@wowsql/sdk 3.4.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 ADDED
@@ -0,0 +1,990 @@
1
+ # WOWSQL TypeScript SDK
2
+
3
+ Official TypeScript/JavaScript SDK for WOWSQL - The powerful MySQL Backend-as-a-Service platform.
4
+
5
+ [![npm version](https://badge.fury.io/js/%40WOWSQL%2Fsdk.svg)](https://www.npmjs.com/package/@wowsql/sdk)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
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
+ ## Installation
18
+
19
+ ```bash
20
+ # npm
21
+ npm install @wowsql/sdk
22
+
23
+ # yarn
24
+ yarn add @wowsql/sdk
25
+
26
+ # pnpm
27
+ pnpm add @wowsql/sdk
28
+ ```
29
+
30
+ ## Quick Start
31
+
32
+ ```typescript
33
+ import WOWSQLClient from '@wowsql/sdk';
34
+
35
+ // Initialize client
36
+ const client = new WOWSQLClient({
37
+ projectUrl: 'myproject', // Your project subdomain
38
+ apiKey: 'your-api-key-here'
39
+ });
40
+
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
53
+
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
+ publicApiKey: 'public-auth-key'
62
+ });
63
+ ```
64
+
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
+ });
74
+
75
+ console.log('New auth user id:', user?.id);
76
+ console.log('Access token:', session.accessToken);
77
+ ```
78
+
79
+ ### Sign In & Persist Sessions
80
+
81
+ ```typescript
82
+ const { session } = await auth.signIn({
83
+ email: 'user@example.com',
84
+ password: 'SuperSecret123'
85
+ });
86
+
87
+ // Save the session and reuse on page refresh
88
+ auth.setSession({
89
+ accessToken: session.accessToken,
90
+ refreshToken: session.refreshToken
91
+ });
92
+
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;
109
+
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
+ );
117
+
118
+ console.log('Logged in as:', result.user?.email);
119
+ console.log('Access token:', result.session.accessToken);
120
+ ```
121
+
122
+ ### Password Reset
123
+
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"
128
+
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"
135
+ ```
136
+
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.):
140
+
141
+ ```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
159
+
160
+ ```typescript
161
+ const client = new WOWSQLClient({
162
+ projectUrl: 'myproject', // Your project subdomain
163
+ apiKey: 'your-api-key' // Your API key from dashboard
164
+ });
165
+ ```
166
+
167
+ ### Advanced Configuration
168
+
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)
176
+ });
177
+ ```
178
+
179
+ ### Using Full URL
180
+
181
+ ```typescript
182
+ const client = new WOWSQLClient({
183
+ projectUrl: 'https://myproject.wowsql.com',
184
+ apiKey: 'your-api-key'
185
+ });
186
+ ```
187
+
188
+ ## Usage Examples
189
+
190
+ ### TypeScript with Generics
191
+
192
+ ```typescript
193
+ interface User {
194
+ id: number;
195
+ name: string;
196
+ email: string;
197
+ age: number;
198
+ created_at: string;
199
+ }
200
+
201
+ const client = new WOWSQLClient({
202
+ projectUrl: 'myproject',
203
+ apiKey: 'your-api-key'
204
+ });
205
+
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
+ ```
212
+
213
+ ### Create Records
214
+
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
+ });
222
+
223
+ console.log(result.id); // New record ID
224
+ ```
225
+
226
+ ### Read Records
227
+
228
+ ```typescript
229
+ // Get all records
230
+ const allUsers = await client.table('users').get();
231
+
232
+ // Get by ID
233
+ const user = await client.table('users').getById(1);
234
+
235
+ // Select specific columns
236
+ const users = await client.table('users')
237
+ .select(['id', 'name', 'email'])
238
+ .get();
239
+
240
+ // With filters
241
+ const adults = await client.table('users')
242
+ .filter({ column: 'age', operator: 'gte', value: 18 })
243
+ .get();
244
+
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' })
249
+ .get();
250
+
251
+ // With sorting
252
+ const sorted = await client.table('users')
253
+ .order('created_at', 'desc')
254
+ .get();
255
+
256
+ // With pagination
257
+ const page1 = await client.table('users')
258
+ .limit(10)
259
+ .offset(0)
260
+ .get();
261
+
262
+ // Get first record
263
+ const firstUser = await client.table('users')
264
+ .filter({ column: 'email', operator: 'eq', value: 'john@example.com' })
265
+ .first();
266
+ ```
267
+
268
+ ### Update Records
269
+
270
+ ```typescript
271
+ // Update by ID
272
+ const result = await client.table('users').update(1, {
273
+ name: 'Jane Doe',
274
+ age: 26
275
+ });
276
+
277
+ console.log(result.affected_rows); // Number of rows updated
278
+ ```
279
+
280
+ ### Delete Records
281
+
282
+ ```typescript
283
+ // Delete by ID
284
+ const result = await client.table('users').delete(1);
285
+
286
+ console.log(result.affected_rows); // Number of rows deleted
287
+ ```
288
+
289
+ ### Filter Operators
290
+
291
+ ```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
+
317
+ ### Complex Queries
318
+
319
+ ```typescript
320
+ // Combine multiple operations
321
+ const result = await client.table<Product>('products')
322
+ .select(['id', 'name', 'price', 'category'])
323
+ .filter({ column: 'category', operator: 'eq', value: 'Electronics' })
324
+ .filter({ column: 'price', operator: 'lt', value: 1000 })
325
+ .filter({ column: 'in_stock', operator: 'eq', value: true })
326
+ .order('price', 'asc')
327
+ .limit(20)
328
+ .offset(0)
329
+ .get();
330
+
331
+ console.log(`Found ${result.total} products`);
332
+ console.log(`Showing ${result.count} products`);
333
+ result.data.forEach(product => {
334
+ console.log(`${product.name}: $${product.price}`);
335
+ });
336
+ ```
337
+
338
+ ### Raw SQL Queries
339
+
340
+ ```typescript
341
+ // Execute custom SQL (read-only)
342
+ const results = await client.query<User>(`
343
+ SELECT id, name, email
344
+ FROM users
345
+ WHERE age > 18
346
+ ORDER BY created_at DESC
347
+ LIMIT 10
348
+ `);
349
+ ```
350
+
351
+ ### Database Metadata
352
+
353
+ ```typescript
354
+ // List all tables
355
+ const tables = await client.listTables();
356
+ console.log(tables); // ['users', 'posts', 'comments']
357
+
358
+ // Get table schema
359
+ const schema = await client.getTableSchema('users');
360
+ console.log(schema.columns);
361
+ console.log(schema.primary_key);
362
+ ```
363
+
364
+ ### Health Check
365
+
366
+ ```typescript
367
+ // Check API health
368
+ const health = await client.health();
369
+ console.log(health.status); // 'ok'
370
+ ```
371
+
372
+ ## Error Handling
373
+
374
+ ```typescript
375
+ import { WOWSQLClient, WOWSQLError } from '@wowsql/sdk';
376
+
377
+ try {
378
+ const user = await client.table('users').getById(999);
379
+ } catch (error) {
380
+ if (error instanceof WOWSQLError) {
381
+ console.error(`Error ${error.statusCode}: ${error.message}`);
382
+ console.error(error.response); // Full error response
383
+ } else {
384
+ console.error('Unexpected error:', error);
385
+ }
386
+ }
387
+ ```
388
+
389
+ ## API Reference
390
+
391
+ ### WOWSQLClient
392
+
393
+ Main client class for interacting with WOWSQL API.
394
+
395
+ #### Methods
396
+
397
+ - `table<T>(tableName: string): Table<T>` - Get table interface
398
+ - `listTables(): Promise<string[]>` - List all tables
399
+ - `getTableSchema(tableName: string): Promise<TableSchema>` - Get table schema
400
+ - `query<T>(sql: string): Promise<T[]>` - Execute raw SQL
401
+ - `health(): Promise<{status: string, timestamp: string}>` - Health check
402
+
403
+ ### Table<T>
404
+
405
+ Fluent interface for table operations.
406
+
407
+ #### Methods
408
+
409
+ - `select(columns: string | string[]): QueryBuilder<T>` - Select columns
410
+ - `filter(filter: FilterExpression): QueryBuilder<T>` - Add filter
411
+ - `get(options?: QueryOptions): Promise<QueryResponse<T>>` - Get records
412
+ - `getById(id: string | number): Promise<T>` - Get by ID
413
+ - `create(data: Partial<T>): Promise<CreateResponse>` - Create record
414
+ - `update(id: string | number, data: Partial<T>): Promise<UpdateResponse>` - Update record
415
+ - `delete(id: string | number): Promise<DeleteResponse>` - Delete record
416
+
417
+ ### QueryBuilder<T>
418
+
419
+ Chainable query builder.
420
+
421
+ #### Methods
422
+
423
+ - `select(columns: string | string[]): this` - Select columns
424
+ - `filter(filter: FilterExpression): this` - Add filter
425
+ - `order(column: string, direction?: 'asc' | 'desc'): this` - Order by
426
+ - `limit(limit: number): this` - Limit results
427
+ - `offset(offset: number): this` - Skip records
428
+ - `get(options?: QueryOptions): Promise<QueryResponse<T>>` - Execute query
429
+ - `first(): Promise<T | null>` - Get first record
430
+
431
+ ## Real-World Examples
432
+
433
+ ### Next.js App Router
434
+
435
+ ```typescript
436
+ // app/api/users/route.ts
437
+ import { NextResponse } from 'next/server';
438
+ import WOWSQLClient from '@wowsql/sdk';
439
+
440
+ const client = new WOWSQLClient({
441
+ projectUrl: process.env.WOWSQL_PROJECT!,
442
+ apiKey: process.env.WOWSQL_API_KEY!
443
+ });
444
+
445
+ export async function GET(request: Request) {
446
+ try {
447
+ const { searchParams } = new URL(request.url);
448
+ const page = parseInt(searchParams.get('page') || '1');
449
+ const limit = 20;
450
+
451
+ const users = await client.table('users')
452
+ .select(['id', 'name', 'email', 'created_at'])
453
+ .order('created_at', 'desc')
454
+ .limit(limit)
455
+ .offset((page - 1) * limit)
456
+ .get();
457
+
458
+ return NextResponse.json(users);
459
+ } catch (error) {
460
+ return NextResponse.json(
461
+ { error: 'Failed to fetch users' },
462
+ { status: 500 }
463
+ );
464
+ }
465
+ }
466
+
467
+ export async function POST(request: Request) {
468
+ try {
469
+ const body = await request.json();
470
+ const result = await client.table('users').create(body);
471
+ return NextResponse.json(result, { status: 201 });
472
+ } catch (error) {
473
+ return NextResponse.json(
474
+ { error: 'Failed to create user' },
475
+ { status: 500 }
476
+ );
477
+ }
478
+ }
479
+ ```
480
+
481
+ ### React Hook
482
+
483
+ ```typescript
484
+ // hooks/useWOWSQL.ts
485
+ import { useState, useEffect } from 'react';
486
+ import WOWSQLClient from '@wowsql/sdk';
487
+
488
+ const client = new WOWSQLClient({
489
+ projectUrl: process.env.NEXT_PUBLIC_WOWSQL_PROJECT!,
490
+ apiKey: process.env.NEXT_PUBLIC_WOWSQL_API_KEY!
491
+ });
492
+
493
+ export function useUsers() {
494
+ const [users, setUsers] = useState([]);
495
+ const [loading, setLoading] = useState(true);
496
+ const [error, setError] = useState(null);
497
+
498
+ useEffect(() => {
499
+ async function fetchUsers() {
500
+ try {
501
+ const result = await client.table('users').get();
502
+ setUsers(result.data);
503
+ } catch (err) {
504
+ setError(err);
505
+ } finally {
506
+ setLoading(false);
507
+ }
508
+ }
509
+ fetchUsers();
510
+ }, []);
511
+
512
+ return { users, loading, error };
513
+ }
514
+ ```
515
+
516
+ ### Express.js API
517
+
518
+ ```typescript
519
+ // server.ts
520
+ import express from 'express';
521
+ import WOWSQLClient from '@wowsql/sdk';
522
+
523
+ const app = express();
524
+ const client = new WOWSQLClient({
525
+ projectUrl: process.env.WOWSQL_PROJECT!,
526
+ apiKey: process.env.WOWSQL_API_KEY!
527
+ });
528
+
529
+ app.use(express.json());
530
+
531
+ // Get all posts
532
+ app.get('/api/posts', async (req, res) => {
533
+ try {
534
+ const { page = 1, limit = 10 } = req.query;
535
+ const posts = await client.table('posts')
536
+ .order('created_at', 'desc')
537
+ .limit(Number(limit))
538
+ .offset((Number(page) - 1) * Number(limit))
539
+ .get();
540
+ res.json(posts);
541
+ } catch (error) {
542
+ res.status(500).json({ error: error.message });
543
+ }
544
+ });
545
+
546
+ // Create post
547
+ app.post('/api/posts', async (req, res) => {
548
+ try {
549
+ const result = await client.table('posts').create(req.body);
550
+ res.status(201).json(result);
551
+ } catch (error) {
552
+ res.status(500).json({ error: error.message });
553
+ }
554
+ });
555
+
556
+ app.listen(3000, () => console.log('Server running on port 3000'));
557
+ ```
558
+
559
+ ## Best Practices
560
+
561
+ ### 1. Environment Variables
562
+
563
+ Never hardcode API keys. Use environment variables:
564
+
565
+ ```typescript
566
+ // .env
567
+ WOWSQL_PROJECT=myproject
568
+ WOWSQL_API_KEY=your-api-key
569
+
570
+ // app.ts
571
+ import WOWSQLClient from '@wowsql/sdk';
572
+
573
+ const client = new WOWSQLClient({
574
+ projectUrl: process.env.WOWSQL_PROJECT!,
575
+ apiKey: process.env.WOWSQL_API_KEY!
576
+ });
577
+ ```
578
+
579
+ ### 2. Singleton Pattern
580
+
581
+ Create a single client instance:
582
+
583
+ ```typescript
584
+ // lib/WOWSQL.ts
585
+ import WOWSQLClient from '@wowsql/sdk';
586
+
587
+ export const db = new WOWSQLClient({
588
+ projectUrl: process.env.WOWSQL_PROJECT!,
589
+ apiKey: process.env.WOWSQL_API_KEY!
590
+ });
591
+
592
+ // Use in other files
593
+ import { db } from './lib/WOWSQL';
594
+ const users = await db.table('users').get();
595
+ ```
596
+
597
+ ### 3. Type Definitions
598
+
599
+ Define interfaces for your data:
600
+
601
+ ```typescript
602
+ // types/database.ts
603
+ export interface User {
604
+ id: number;
605
+ name: string;
606
+ email: string;
607
+ created_at: string;
608
+ }
609
+
610
+ export interface Post {
611
+ id: number;
612
+ user_id: number;
613
+ title: string;
614
+ content: string;
615
+ published_at: string | null;
616
+ }
617
+
618
+ // Use in queries
619
+ const users = await db.table<User>('users').get();
620
+ ```
621
+
622
+ ### 4. Error Handling
623
+
624
+ Always wrap API calls in try-catch:
625
+
626
+ ```typescript
627
+ import { WOWSQLError } from '@wowsql/sdk';
628
+
629
+ async function createUser(data: any) {
630
+ try {
631
+ const result = await db.table('users').create(data);
632
+ return { success: true, data: result };
633
+ } catch (error) {
634
+ if (error instanceof WOWSQLError) {
635
+ console.error(`Database error: ${error.message}`);
636
+ return { success: false, error: error.message };
637
+ }
638
+ throw error;
639
+ }
640
+ }
641
+ ```
642
+
643
+ ## API Keys
644
+
645
+ WOWSQL uses **different API keys for different operations**. Understanding which key to use is crucial for proper authentication.
646
+
647
+ ### Key Types Overview
648
+
649
+ | Operation Type | Recommended Key | Alternative Key | Used By |
650
+ |---------------|----------------|-----------------|---------|
651
+ | **Database Operations** (CRUD) | Service Role Key (`wowbase_service_...`) | Anonymous Key (`wowbase_anon_...`) | `WOWSQLClient` |
652
+ | **Authentication Operations** (OAuth, sign-in) | Public API Key (`wowbase_auth_...`) | Service Role Key (`wowbase_service_...`) | `ProjectAuthClient` |
653
+
654
+ ### Where to Find Your Keys
655
+
656
+ All keys are found in: **WOWSQL Dashboard → Authentication → PROJECT KEYS**
657
+
658
+ 1. **Service Role Key** (`wowbase_service_...`)
659
+ - Location: "Service Role Key (keep secret)"
660
+ - Used for: Database CRUD operations (recommended for server-side)
661
+ - Can also be used for authentication operations (fallback)
662
+ - **Important**: Click the eye icon to reveal this key
663
+
664
+ 2. **Public API Key** (`wowbase_auth_...`)
665
+ - Location: "Public API Key"
666
+ - Used for: OAuth, sign-in, sign-up, user management
667
+ - Recommended for client-side/public authentication flows
668
+
669
+ 3. **Anonymous Key** (`wowbase_anon_...`)
670
+ - Location: "Anonymous Key"
671
+ - Used for: Public/client-side database operations with limited permissions
672
+ - Optional: Use when exposing database access to frontend/client
673
+
674
+ ### Database Operations
675
+
676
+ Use **Service Role Key** or **Anonymous Key** for database operations:
677
+
678
+ ```typescript
679
+ import WOWSQLClient from '@wowsql/sdk';
680
+
681
+ // Using Service Role Key (recommended for server-side, full access)
682
+ const client = new WOWSQLClient({
683
+ projectUrl: 'myproject',
684
+ apiKey: 'wowbase_service_your-service-key-here' // Service Role Key
685
+ });
686
+
687
+ // Using Anonymous Key (for public/client-side access with limited permissions)
688
+ const client = new WOWSQLClient({
689
+ projectUrl: 'myproject',
690
+ apiKey: 'wowbase_anon_your-anon-key-here' // Anonymous Key
691
+ });
692
+
693
+ // Query data
694
+ const users = await client.table('users').get();
695
+ ```
696
+
697
+ ### Authentication Operations
698
+
699
+ Use **Public API Key** or **Service Role Key** for authentication:
700
+
701
+ ```typescript
702
+ import { ProjectAuthClient } from '@wowsql/sdk';
703
+
704
+ // Using Public API Key (recommended for OAuth, sign-in, sign-up)
705
+ const auth = new ProjectAuthClient({
706
+ projectUrl: 'myproject',
707
+ publicApiKey: 'wowbase_auth_your-public-key-here' // Public API Key
708
+ });
709
+
710
+ // Using Service Role Key (can be used for auth operations too)
711
+ const auth = new ProjectAuthClient({
712
+ projectUrl: 'myproject',
713
+ publicApiKey: 'wowbase_service_your-service-key-here' // Service Role Key
714
+ });
715
+
716
+ // OAuth authentication
717
+ const { authorizationUrl } = await auth.getOAuthAuthorizationUrl(
718
+ 'github',
719
+ 'https://app.example.com/auth/callback'
720
+ );
721
+ ```
722
+
723
+ ### Environment Variables
724
+
725
+ Best practice: Use environment variables for API keys:
726
+
727
+ ```typescript
728
+ // Database operations - Service Role Key
729
+ const dbClient = new WOWSQLClient({
730
+ projectUrl: process.env.WOWSQL_PROJECT_URL!,
731
+ apiKey: process.env.WOWSQL_SERVICE_ROLE_KEY! // or WOWSQL_ANON_KEY
732
+ });
733
+
734
+ // Authentication operations - Public API Key
735
+ const authClient = new ProjectAuthClient({
736
+ projectUrl: process.env.WOWSQL_PROJECT_URL!,
737
+ publicApiKey: process.env.WOWSQL_PUBLIC_API_KEY!
738
+ });
739
+ ```
740
+
741
+ ### Key Usage Summary
742
+
743
+ - **`WOWSQLClient`** → Uses **Service Role Key** or **Anonymous Key** for database operations
744
+ - **`ProjectAuthClient`** → Uses **Public API Key** or **Service Role Key** for authentication operations
745
+ - **Service Role Key** can be used for both database AND authentication operations
746
+ - **Public API Key** is specifically for authentication operations only
747
+ - **Anonymous Key** is optional and provides limited permissions for public database access
748
+
749
+ ### Security Best Practices
750
+
751
+ 1. **Never expose Service Role Key** in client-side code or public repositories
752
+ 2. **Use Public API Key** for client-side authentication flows
753
+ 3. **Use Anonymous Key** for public database access with limited permissions
754
+ 4. **Store keys in environment variables**, never hardcode them
755
+ 5. **Rotate keys regularly** if compromised
756
+
757
+ ### Troubleshooting
758
+
759
+ **Error: "Invalid API key for project"**
760
+ - Ensure you're using the correct key type for the operation
761
+ - Database operations require Service Role Key or Anonymous Key
762
+ - Authentication operations require Public API Key or Service Role Key
763
+ - Verify the key is copied correctly (no extra spaces)
764
+
765
+ **Error: "Authentication failed"**
766
+ - Check that you're using Public API Key (not Anonymous Key) for auth operations
767
+ - Verify the project URL matches your dashboard
768
+ - Ensure the key hasn't been revoked or expired
769
+
770
+ ## 🔧 Schema Management
771
+
772
+ Programmatically manage your database schema with the `WOWSQLSchema` client.
773
+
774
+ > **⚠️ IMPORTANT**: Schema operations require a **Service Role Key** (`service_*`). Anonymous keys will return a 403 Forbidden error.
775
+
776
+ ### Quick Start
777
+
778
+ ```typescript
779
+ import { WOWSQLSchema } from '@wowsql/sdk';
780
+
781
+ // Initialize schema client with SERVICE ROLE KEY
782
+ const schema = new WOWSQLSchema(
783
+ 'https://your-project.wowsql.com',
784
+ 'service_xyz789...' // ⚠️ Backend only! Never expose!
785
+ );
786
+ ```
787
+
788
+ ### Create Table
789
+
790
+ ```typescript
791
+ // Create a new table
792
+ await schema.createTable({
793
+ tableName: 'products',
794
+ columns: [
795
+ { name: 'id', type: 'INT', auto_increment: true },
796
+ { name: 'name', type: 'VARCHAR(255)', not_null: true },
797
+ { name: 'price', type: 'DECIMAL(10,2)', not_null: true },
798
+ { name: 'category', type: 'VARCHAR(100)' },
799
+ { name: 'created_at', type: 'TIMESTAMP', default: 'CURRENT_TIMESTAMP' }
800
+ ],
801
+ primaryKey: 'id',
802
+ indexes: [
803
+ { name: 'idx_category', columns: ['category'] },
804
+ { name: 'idx_price', columns: ['price'] }
805
+ ]
806
+ });
807
+
808
+ console.log('Table created successfully!');
809
+ ```
810
+
811
+ ### Alter Table
812
+
813
+ ```typescript
814
+ // Add a new column
815
+ await schema.alterTable({
816
+ tableName: 'products',
817
+ addColumns: [
818
+ { name: 'stock_quantity', type: 'INT', default: '0' }
819
+ ]
820
+ });
821
+
822
+ // Modify an existing column
823
+ await schema.alterTable({
824
+ tableName: 'products',
825
+ modifyColumns: [
826
+ { name: 'price', type: 'DECIMAL(12,2)' } // Increase precision
827
+ ]
828
+ });
829
+
830
+ // Drop a column
831
+ await schema.alterTable({
832
+ tableName: 'products',
833
+ dropColumns: ['category']
834
+ });
835
+
836
+ // Rename a column
837
+ await schema.alterTable({
838
+ tableName: 'products',
839
+ renameColumns: [
840
+ { oldName: 'name', newName: 'product_name' }
841
+ ]
842
+ });
843
+ ```
844
+
845
+ ### Drop Table
846
+
847
+ ```typescript
848
+ // Drop a table
849
+ await schema.dropTable('old_table');
850
+
851
+ // Drop with CASCADE (removes dependent objects)
852
+ await schema.dropTable('products', { cascade: true });
853
+ ```
854
+
855
+ ### Execute Raw SQL
856
+
857
+ ```typescript
858
+ // Execute custom schema SQL
859
+ await schema.executeSQL(`
860
+ CREATE INDEX idx_product_name
861
+ ON products(product_name);
862
+ `);
863
+
864
+ // Add a foreign key constraint
865
+ await schema.executeSQL(`
866
+ ALTER TABLE orders
867
+ ADD CONSTRAINT fk_product
868
+ FOREIGN KEY (product_id)
869
+ REFERENCES products(id);
870
+ `);
871
+ ```
872
+
873
+ ### Security & Best Practices
874
+
875
+ #### ✅ DO:
876
+ - Use service role keys **only in backend/server code** (Node.js, Next.js API routes)
877
+ - Store service keys in environment variables
878
+ - Use anonymous keys for client-side data operations
879
+ - Test schema changes in development first
880
+
881
+ #### ❌ DON'T:
882
+ - Never expose service role keys in frontend/browser code
883
+ - Never commit service keys to version control
884
+ - Don't use anonymous keys for schema operations (will fail)
885
+
886
+ ### Example: Next.js API Route Migration
887
+
888
+ ```typescript
889
+ // app/api/migrate/route.ts
890
+ import { WOWSQLSchema } from '@wowsql/sdk';
891
+ import { NextResponse } from 'next/server';
892
+
893
+ export async function POST() {
894
+ const schema = new WOWSQLSchema(
895
+ process.env.WOWSQL_PROJECT_URL!,
896
+ process.env.WOWSQL_SERVICE_KEY! // From env var
897
+ );
898
+
899
+ try {
900
+ // Create users table
901
+ await schema.createTable({
902
+ tableName: 'users',
903
+ columns: [
904
+ { name: 'id', type: 'INT', auto_increment: true },
905
+ { name: 'email', type: 'VARCHAR(255)', unique: true, not_null: true },
906
+ { name: 'name', type: 'VARCHAR(255)', not_null: true },
907
+ { name: 'created_at', type: 'TIMESTAMP', default: 'CURRENT_TIMESTAMP' }
908
+ ],
909
+ primaryKey: 'id',
910
+ indexes: [{ name: 'idx_email', columns: ['email'] }]
911
+ });
912
+
913
+ return NextResponse.json({ success: true });
914
+ } catch (error) {
915
+ return NextResponse.json({ error: error.message }, { status: 500 });
916
+ }
917
+ }
918
+ ```
919
+
920
+ ### Error Handling
921
+
922
+ ```typescript
923
+ import { WOWSQLSchema, PermissionError } from '@wowsql/sdk';
924
+
925
+ try {
926
+ const schema = new WOWSQLSchema(
927
+ 'https://your-project.wowsql.com',
928
+ 'service_xyz...'
929
+ );
930
+
931
+ await schema.createTable({
932
+ tableName: 'test',
933
+ columns: [{ name: 'id', type: 'INT' }]
934
+ });
935
+ } catch (error) {
936
+ if (error instanceof PermissionError) {
937
+ console.error('Permission denied:', error.message);
938
+ console.error('Make sure you\'re using a SERVICE ROLE KEY!');
939
+ } else {
940
+ console.error('Error:', error);
941
+ }
942
+ }
943
+ ```
944
+
945
+ ---
946
+
947
+ ## FAQ
948
+
949
+ ### Can I use this in the browser?
950
+
951
+ 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.
952
+
953
+ ### What about rate limits?
954
+
955
+ Rate limits depend on your WOWSQL plan. The SDK will throw a `WOWSQLError` with status code 429 when rate limits are exceeded.
956
+
957
+ ### Does it support transactions?
958
+
959
+ Currently, the SDK doesn't support transactions directly. Use raw SQL queries for complex transactional operations.
960
+
961
+ ### How do I upgrade?
962
+
963
+ ```bash
964
+ npm update @wowsql/sdk
965
+ ```
966
+
967
+ ## Support
968
+
969
+ - 📧 Email: support@wowsql.com
970
+ - 💬 Discord: [Join our community](https://discord.gg/WOWSQL)
971
+ - 📚 Documentation: [docs.wowsql.com](https://docs.wowsql.com)
972
+ - 🐛 Issues: [GitHub Issues](https://github.com/wowsql/wowsql/issues)
973
+
974
+ ## Contributing
975
+
976
+ Contributions are welcome! Please see [CONTRIBUTING.md](../../CONTRIBUTING.md) for details.
977
+
978
+ ## License
979
+
980
+ MIT License - see [LICENSE](LICENSE) file for details.
981
+
982
+ ## Changelog
983
+
984
+ See [CHANGELOG.md](CHANGELOG.md) for version history.
985
+
986
+ ---
987
+
988
+ Made with ❤️ by the WOWSQL Team
989
+
990
+