@tspvivek/baasix-sdk 0.1.0-alpha.1
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/LICENSE +21 -0
- package/README.md +942 -0
- package/dist/client-CzF9B60b.d.ts +614 -0
- package/dist/client-aXK_gEyr.d.cts +614 -0
- package/dist/index.cjs +4159 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +1498 -0
- package/dist/index.d.ts +1498 -0
- package/dist/index.js +4135 -0
- package/dist/index.js.map +1 -0
- package/dist/modules/auth.cjs +651 -0
- package/dist/modules/auth.cjs.map +1 -0
- package/dist/modules/auth.d.cts +384 -0
- package/dist/modules/auth.d.ts +384 -0
- package/dist/modules/auth.js +649 -0
- package/dist/modules/auth.js.map +1 -0
- package/dist/modules/files.cjs +266 -0
- package/dist/modules/files.cjs.map +1 -0
- package/dist/modules/files.d.cts +187 -0
- package/dist/modules/files.d.ts +187 -0
- package/dist/modules/files.js +264 -0
- package/dist/modules/files.js.map +1 -0
- package/dist/modules/items.cjs +654 -0
- package/dist/modules/items.cjs.map +1 -0
- package/dist/modules/items.d.cts +472 -0
- package/dist/modules/items.d.ts +472 -0
- package/dist/modules/items.js +651 -0
- package/dist/modules/items.js.map +1 -0
- package/dist/modules/schemas.cjs +269 -0
- package/dist/modules/schemas.cjs.map +1 -0
- package/dist/modules/schemas.d.cts +239 -0
- package/dist/modules/schemas.d.ts +239 -0
- package/dist/modules/schemas.js +267 -0
- package/dist/modules/schemas.js.map +1 -0
- package/dist/storage/index.cjs +162 -0
- package/dist/storage/index.cjs.map +1 -0
- package/dist/storage/index.d.cts +96 -0
- package/dist/storage/index.d.ts +96 -0
- package/dist/storage/index.js +157 -0
- package/dist/storage/index.js.map +1 -0
- package/dist/types-BdjsGANq.d.cts +40 -0
- package/dist/types-BdjsGANq.d.ts +40 -0
- package/package.json +107 -0
package/README.md
ADDED
|
@@ -0,0 +1,942 @@
|
|
|
1
|
+
# @tspvivek/baasix-sdk
|
|
2
|
+
|
|
3
|
+
Official JavaScript/TypeScript SDK for [Baasix](https://www.baasix.com) Backend-as-a-Service.
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/@tspvivek/baasix-sdk)
|
|
6
|
+
[](https://www.npmjs.com/package/@tspvivek/baasix-sdk)
|
|
7
|
+
[](https://github.com/tspvivek/baasix-sdk/blob/main/LICENSE)
|
|
8
|
+
|
|
9
|
+
## Features
|
|
10
|
+
|
|
11
|
+
- 🌐 **Universal** - Works in browsers, Node.js, and React Native
|
|
12
|
+
- 🔐 **Flexible Auth** - JWT tokens, HTTP-only cookies, OAuth (Google, Facebook, Apple, GitHub)
|
|
13
|
+
- 💾 **Customizable Storage** - LocalStorage, AsyncStorage, or custom adapters
|
|
14
|
+
- 📝 **Type-Safe** - Full TypeScript support with generics
|
|
15
|
+
- 🔄 **Auto Token Refresh** - Seamless token management
|
|
16
|
+
- 🏢 **Multi-Tenant** - Built-in tenant switching and invitation support
|
|
17
|
+
- ⚡ **Query Builder** - Fluent API for complex queries with 50+ filter operators
|
|
18
|
+
- 📡 **Realtime** - WebSocket subscriptions for live data updates
|
|
19
|
+
- 📁 **File Management** - Upload, download, and transform assets
|
|
20
|
+
- 🔀 **Workflows** - Execute and monitor workflow executions
|
|
21
|
+
- 👥 **User & Role Management** - Admin operations for users and roles
|
|
22
|
+
- 📊 **Reports** - Generate reports with aggregations
|
|
23
|
+
|
|
24
|
+
## Installation
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
npm install @tspvivek/baasix-sdk
|
|
28
|
+
# or
|
|
29
|
+
yarn add @tspvivek/baasix-sdk
|
|
30
|
+
# or
|
|
31
|
+
pnpm add @tspvivek/baasix-sdk
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Quick Start
|
|
35
|
+
|
|
36
|
+
```typescript
|
|
37
|
+
import { createBaasix } from '@tspvivek/baasix-sdk';
|
|
38
|
+
|
|
39
|
+
// Create client
|
|
40
|
+
const baasix = createBaasix({
|
|
41
|
+
url: 'https://your-baasix-instance.com',
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
// Login
|
|
45
|
+
const { user, token } = await baasix.auth.login({
|
|
46
|
+
email: 'user@example.com',
|
|
47
|
+
password: 'password123',
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
// Query items
|
|
51
|
+
const { data: products } = await baasix.items('products').find({
|
|
52
|
+
filter: { status: { eq: 'active' } },
|
|
53
|
+
sort: { createdAt: 'desc' },
|
|
54
|
+
limit: 10,
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
// Create item
|
|
58
|
+
const productId = await baasix.items('products').create({
|
|
59
|
+
name: 'New Product',
|
|
60
|
+
price: 29.99,
|
|
61
|
+
});
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Configuration
|
|
65
|
+
|
|
66
|
+
### Basic Configuration
|
|
67
|
+
|
|
68
|
+
```typescript
|
|
69
|
+
import { createBaasix } from '@tspvivek/baasix-sdk';
|
|
70
|
+
|
|
71
|
+
const baasix = createBaasix({
|
|
72
|
+
url: 'https://api.example.com', // Required: Your Baasix URL
|
|
73
|
+
authMode: 'jwt', // 'jwt' (default) or 'cookie'
|
|
74
|
+
timeout: 30000, // Request timeout in ms (default: 30000)
|
|
75
|
+
autoRefresh: true, // Auto-refresh tokens (default: true)
|
|
76
|
+
onAuthStateChange: (event, user) => { // Auth state callback
|
|
77
|
+
console.log('Auth changed:', event, user);
|
|
78
|
+
},
|
|
79
|
+
});
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### React Native Setup
|
|
83
|
+
|
|
84
|
+
```typescript
|
|
85
|
+
import { createBaasix, AsyncStorageAdapter } from '@tspvivek/baasix-sdk';
|
|
86
|
+
import AsyncStorage from '@react-native-async-storage/async-storage';
|
|
87
|
+
|
|
88
|
+
const baasix = createBaasix({
|
|
89
|
+
url: 'https://api.example.com',
|
|
90
|
+
storage: new AsyncStorageAdapter(AsyncStorage),
|
|
91
|
+
});
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Cookie Mode (Web with HTTP-only cookies)
|
|
95
|
+
|
|
96
|
+
```typescript
|
|
97
|
+
const baasix = createBaasix({
|
|
98
|
+
url: 'https://api.example.com',
|
|
99
|
+
authMode: 'cookie',
|
|
100
|
+
credentials: 'include', // Required for cookies
|
|
101
|
+
});
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### Server-Side / Service Account
|
|
105
|
+
|
|
106
|
+
```typescript
|
|
107
|
+
const baasix = createBaasix({
|
|
108
|
+
url: 'https://api.example.com',
|
|
109
|
+
token: 'your-service-account-token', // Static token
|
|
110
|
+
});
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## Authentication
|
|
114
|
+
|
|
115
|
+
### Register
|
|
116
|
+
|
|
117
|
+
```typescript
|
|
118
|
+
const { user, token } = await baasix.auth.register({
|
|
119
|
+
email: 'newuser@example.com',
|
|
120
|
+
password: 'securepassword',
|
|
121
|
+
firstName: 'John',
|
|
122
|
+
lastName: 'Doe',
|
|
123
|
+
});
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### Login
|
|
127
|
+
|
|
128
|
+
```typescript
|
|
129
|
+
const { user, token } = await baasix.auth.login({
|
|
130
|
+
email: 'user@example.com',
|
|
131
|
+
password: 'password123',
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
// With tenant (multi-tenant mode)
|
|
135
|
+
const result = await baasix.auth.login({
|
|
136
|
+
email: 'user@example.com',
|
|
137
|
+
password: 'password123',
|
|
138
|
+
tenantId: 'tenant-uuid',
|
|
139
|
+
});
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### Get Current User
|
|
143
|
+
|
|
144
|
+
```typescript
|
|
145
|
+
// From server (makes API call)
|
|
146
|
+
const user = await baasix.auth.getUser();
|
|
147
|
+
|
|
148
|
+
// From cache (no API call)
|
|
149
|
+
const cachedUser = await baasix.auth.getCachedUser();
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### Logout
|
|
153
|
+
|
|
154
|
+
```typescript
|
|
155
|
+
await baasix.auth.logout();
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### Check Authentication
|
|
159
|
+
|
|
160
|
+
```typescript
|
|
161
|
+
if (await baasix.auth.isAuthenticated()) {
|
|
162
|
+
// User is logged in
|
|
163
|
+
}
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### Magic Link Login
|
|
167
|
+
|
|
168
|
+
```typescript
|
|
169
|
+
// Send magic link
|
|
170
|
+
await baasix.auth.sendMagicLink({
|
|
171
|
+
email: 'user@example.com',
|
|
172
|
+
redirectUrl: 'https://myapp.com/auth/callback',
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
// Verify (after user clicks link)
|
|
176
|
+
const { user, token } = await baasix.auth.verifyMagicLink('verification-token');
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### Password Reset
|
|
180
|
+
|
|
181
|
+
```typescript
|
|
182
|
+
// Request reset
|
|
183
|
+
await baasix.auth.forgotPassword({
|
|
184
|
+
email: 'user@example.com',
|
|
185
|
+
redirectUrl: 'https://myapp.com/reset-password',
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
// Reset password
|
|
189
|
+
await baasix.auth.resetPassword('reset-token', 'newpassword123');
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
### Multi-Tenant
|
|
193
|
+
|
|
194
|
+
```typescript
|
|
195
|
+
// Get available tenants
|
|
196
|
+
const tenants = await baasix.auth.getTenants();
|
|
197
|
+
|
|
198
|
+
// Switch tenant
|
|
199
|
+
const { user, token } = await baasix.auth.switchTenant('tenant-uuid');
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
## Items (CRUD Operations)
|
|
203
|
+
|
|
204
|
+
### Query Items
|
|
205
|
+
|
|
206
|
+
```typescript
|
|
207
|
+
const items = baasix.items('products');
|
|
208
|
+
|
|
209
|
+
// Basic find
|
|
210
|
+
const { data, totalCount } = await items.find();
|
|
211
|
+
|
|
212
|
+
// With parameters
|
|
213
|
+
const { data: activeProducts } = await items.find({
|
|
214
|
+
filter: { status: { eq: 'active' } },
|
|
215
|
+
sort: { createdAt: 'desc' },
|
|
216
|
+
limit: 20,
|
|
217
|
+
page: 1,
|
|
218
|
+
fields: ['id', 'name', 'price', 'category.*'],
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
// Find one by ID
|
|
222
|
+
const product = await items.findOne('product-uuid');
|
|
223
|
+
|
|
224
|
+
// With related data
|
|
225
|
+
const product = await items.findOne('product-uuid', {
|
|
226
|
+
fields: ['*', 'category.*', 'reviews.*'],
|
|
227
|
+
});
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
### Query Builder
|
|
231
|
+
|
|
232
|
+
```typescript
|
|
233
|
+
const results = await baasix.items('posts')
|
|
234
|
+
.query()
|
|
235
|
+
.select('*', 'author.*', 'comments.*')
|
|
236
|
+
.filter({
|
|
237
|
+
AND: [
|
|
238
|
+
{ status: { eq: 'published' } },
|
|
239
|
+
{ createdAt: { gte: '$NOW-DAYS_30' } },
|
|
240
|
+
],
|
|
241
|
+
})
|
|
242
|
+
.sort({ createdAt: 'desc' })
|
|
243
|
+
.limit(10)
|
|
244
|
+
.page(1)
|
|
245
|
+
.get();
|
|
246
|
+
|
|
247
|
+
// First result only
|
|
248
|
+
const post = await baasix.items('posts')
|
|
249
|
+
.query()
|
|
250
|
+
.filter({ slug: { eq: 'my-post' } })
|
|
251
|
+
.first();
|
|
252
|
+
|
|
253
|
+
// Count
|
|
254
|
+
const count = await baasix.items('products')
|
|
255
|
+
.query()
|
|
256
|
+
.filter({ inStock: { eq: true } })
|
|
257
|
+
.count();
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
### Create Items
|
|
261
|
+
|
|
262
|
+
```typescript
|
|
263
|
+
// Single item
|
|
264
|
+
const id = await baasix.items('products').create({
|
|
265
|
+
name: 'New Product',
|
|
266
|
+
price: 29.99,
|
|
267
|
+
status: 'draft',
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
// Multiple items
|
|
271
|
+
const ids = await baasix.items('products').createMany([
|
|
272
|
+
{ name: 'Product 1', price: 10 },
|
|
273
|
+
{ name: 'Product 2', price: 20 },
|
|
274
|
+
]);
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
### Update Items
|
|
278
|
+
|
|
279
|
+
```typescript
|
|
280
|
+
// Single item
|
|
281
|
+
await baasix.items('products').update('product-uuid', {
|
|
282
|
+
price: 24.99,
|
|
283
|
+
status: 'published',
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
// Multiple items
|
|
287
|
+
await baasix.items('products').updateMany(
|
|
288
|
+
['id1', 'id2', 'id3'],
|
|
289
|
+
{ status: 'archived' }
|
|
290
|
+
);
|
|
291
|
+
|
|
292
|
+
// Upsert (create or update)
|
|
293
|
+
const id = await baasix.items('products').upsert(
|
|
294
|
+
{ sku: { eq: 'SKU-001' } },
|
|
295
|
+
{ name: 'Widget', price: 29.99, sku: 'SKU-001' }
|
|
296
|
+
);
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
### Delete Items
|
|
300
|
+
|
|
301
|
+
```typescript
|
|
302
|
+
// Single item
|
|
303
|
+
await baasix.items('products').delete('product-uuid');
|
|
304
|
+
|
|
305
|
+
// Multiple items
|
|
306
|
+
await baasix.items('products').deleteMany(['id1', 'id2', 'id3']);
|
|
307
|
+
|
|
308
|
+
// Soft delete (if paranoid mode enabled)
|
|
309
|
+
await baasix.items('products').softDelete('product-uuid');
|
|
310
|
+
|
|
311
|
+
// Restore soft-deleted
|
|
312
|
+
await baasix.items('products').restore('product-uuid');
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
### Aggregation
|
|
316
|
+
|
|
317
|
+
```typescript
|
|
318
|
+
const results = await baasix.items('orders').aggregate({
|
|
319
|
+
aggregate: {
|
|
320
|
+
totalRevenue: { function: 'sum', field: 'total' },
|
|
321
|
+
orderCount: { function: 'count', field: 'id' },
|
|
322
|
+
avgOrderValue: { function: 'avg', field: 'total' },
|
|
323
|
+
},
|
|
324
|
+
groupBy: ['status', 'category'],
|
|
325
|
+
filter: { createdAt: { gte: '$NOW-DAYS_30' } },
|
|
326
|
+
});
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
## Filter Operators
|
|
330
|
+
|
|
331
|
+
Baasix supports 50+ filter operators:
|
|
332
|
+
|
|
333
|
+
```typescript
|
|
334
|
+
// Comparison
|
|
335
|
+
{ field: { eq: value } } // Equal
|
|
336
|
+
{ field: { ne: value } } // Not equal
|
|
337
|
+
{ field: { gt: value } } // Greater than
|
|
338
|
+
{ field: { gte: value } } // Greater than or equal
|
|
339
|
+
{ field: { lt: value } } // Less than
|
|
340
|
+
{ field: { lte: value } } // Less than or equal
|
|
341
|
+
|
|
342
|
+
// Collection
|
|
343
|
+
{ field: { in: [1, 2, 3] } } // In list
|
|
344
|
+
{ field: { notIn: [1, 2, 3] } } // Not in list
|
|
345
|
+
|
|
346
|
+
// String
|
|
347
|
+
{ field: { like: 'pattern' } } // LIKE (case-sensitive)
|
|
348
|
+
{ field: { iLike: 'pattern' } } // ILIKE (case-insensitive)
|
|
349
|
+
{ field: { startsWith: 'prefix' } } // Starts with
|
|
350
|
+
{ field: { endsWith: 'suffix' } } // Ends with
|
|
351
|
+
{ field: { contains: 'substring' } } // Contains
|
|
352
|
+
|
|
353
|
+
// Range
|
|
354
|
+
{ field: { between: [10, 100] } } // Between
|
|
355
|
+
|
|
356
|
+
// Null
|
|
357
|
+
{ field: { isNull: true } } // Is null
|
|
358
|
+
{ field: { isNotNull: true } } // Is not null
|
|
359
|
+
|
|
360
|
+
// Array (PostgreSQL)
|
|
361
|
+
{ tags: { arraycontains: ['js', 'api'] } }
|
|
362
|
+
|
|
363
|
+
// JSONB
|
|
364
|
+
{ metadata: { jsonbHasKey: 'discount' } }
|
|
365
|
+
{ metadata: { jsonbKeyEquals: { key: 'status', value: 'active' } } }
|
|
366
|
+
|
|
367
|
+
// Logical
|
|
368
|
+
{ AND: [{ status: { eq: 'active' } }, { price: { gt: 0 } }] }
|
|
369
|
+
{ OR: [{ status: { eq: 'featured' } }, { views: { gt: 1000 } }] }
|
|
370
|
+
|
|
371
|
+
// Relation filtering
|
|
372
|
+
{ 'author.name': { like: 'John' } }
|
|
373
|
+
|
|
374
|
+
// Dynamic variables
|
|
375
|
+
{ author_Id: { eq: '$CURRENT_USER' } }
|
|
376
|
+
{ createdAt: { gte: '$NOW-DAYS_30' } }
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
## Files
|
|
380
|
+
|
|
381
|
+
### Upload Files
|
|
382
|
+
|
|
383
|
+
```typescript
|
|
384
|
+
// Browser
|
|
385
|
+
const fileMetadata = await baasix.files.upload(fileInput.files[0], {
|
|
386
|
+
title: 'Product Image',
|
|
387
|
+
folder: 'products',
|
|
388
|
+
isPublic: true,
|
|
389
|
+
onProgress: (progress) => console.log(`${progress}% uploaded`),
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
// React Native with expo-image-picker
|
|
393
|
+
const metadata = await baasix.files.upload({
|
|
394
|
+
uri: result.uri,
|
|
395
|
+
name: 'photo.jpg',
|
|
396
|
+
type: 'image/jpeg',
|
|
397
|
+
});
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
### Get Asset URLs
|
|
401
|
+
|
|
402
|
+
```typescript
|
|
403
|
+
// Original file
|
|
404
|
+
const url = baasix.files.getAssetUrl('file-uuid');
|
|
405
|
+
|
|
406
|
+
// With transformations
|
|
407
|
+
const thumbnailUrl = baasix.files.getAssetUrl('file-uuid', {
|
|
408
|
+
width: 200,
|
|
409
|
+
height: 200,
|
|
410
|
+
fit: 'cover',
|
|
411
|
+
quality: 80,
|
|
412
|
+
format: 'webp',
|
|
413
|
+
});
|
|
414
|
+
```
|
|
415
|
+
|
|
416
|
+
### File Operations
|
|
417
|
+
|
|
418
|
+
```typescript
|
|
419
|
+
// List files
|
|
420
|
+
const { data: files } = await baasix.files.find({
|
|
421
|
+
filter: { mimeType: { startsWith: 'image/' } },
|
|
422
|
+
});
|
|
423
|
+
|
|
424
|
+
// Get file info
|
|
425
|
+
const file = await baasix.files.findOne('file-uuid');
|
|
426
|
+
|
|
427
|
+
// Download file
|
|
428
|
+
const blob = await baasix.files.download('file-uuid');
|
|
429
|
+
|
|
430
|
+
// Delete file
|
|
431
|
+
await baasix.files.delete('file-uuid');
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
## Schemas
|
|
435
|
+
|
|
436
|
+
### Create Collection
|
|
437
|
+
|
|
438
|
+
```typescript
|
|
439
|
+
await baasix.schemas.create({
|
|
440
|
+
collectionName: 'products',
|
|
441
|
+
schema: {
|
|
442
|
+
name: 'Product',
|
|
443
|
+
timestamps: true,
|
|
444
|
+
paranoid: true, // Soft deletes
|
|
445
|
+
fields: {
|
|
446
|
+
id: {
|
|
447
|
+
type: 'UUID',
|
|
448
|
+
primaryKey: true,
|
|
449
|
+
defaultValue: { type: 'UUIDV4' },
|
|
450
|
+
},
|
|
451
|
+
name: {
|
|
452
|
+
type: 'String',
|
|
453
|
+
allowNull: false,
|
|
454
|
+
values: { length: 255 },
|
|
455
|
+
},
|
|
456
|
+
price: {
|
|
457
|
+
type: 'Decimal',
|
|
458
|
+
values: { precision: 10, scale: 2 },
|
|
459
|
+
defaultValue: 0,
|
|
460
|
+
},
|
|
461
|
+
tags: {
|
|
462
|
+
type: 'Array',
|
|
463
|
+
values: { type: 'String' },
|
|
464
|
+
defaultValue: [],
|
|
465
|
+
},
|
|
466
|
+
metadata: {
|
|
467
|
+
type: 'JSONB',
|
|
468
|
+
defaultValue: {},
|
|
469
|
+
},
|
|
470
|
+
},
|
|
471
|
+
},
|
|
472
|
+
});
|
|
473
|
+
```
|
|
474
|
+
|
|
475
|
+
### Relationships
|
|
476
|
+
|
|
477
|
+
```typescript
|
|
478
|
+
// Many-to-One (BelongsTo)
|
|
479
|
+
await baasix.schemas.createRelationship('products', {
|
|
480
|
+
type: 'M2O',
|
|
481
|
+
target: 'categories',
|
|
482
|
+
name: 'category',
|
|
483
|
+
alias: 'products',
|
|
484
|
+
});
|
|
485
|
+
|
|
486
|
+
// Many-to-Many
|
|
487
|
+
await baasix.schemas.createRelationship('products', {
|
|
488
|
+
type: 'M2M',
|
|
489
|
+
target: 'tags',
|
|
490
|
+
name: 'tags',
|
|
491
|
+
alias: 'products',
|
|
492
|
+
});
|
|
493
|
+
```
|
|
494
|
+
|
|
495
|
+
### Indexes
|
|
496
|
+
|
|
497
|
+
```typescript
|
|
498
|
+
await baasix.schemas.createIndex('products', {
|
|
499
|
+
name: 'idx_products_sku',
|
|
500
|
+
fields: ['sku'],
|
|
501
|
+
unique: true,
|
|
502
|
+
});
|
|
503
|
+
```
|
|
504
|
+
|
|
505
|
+
## Reports & Analytics
|
|
506
|
+
|
|
507
|
+
```typescript
|
|
508
|
+
// Generate report
|
|
509
|
+
const report = await baasix.reports.generate('orders', {
|
|
510
|
+
aggregate: {
|
|
511
|
+
revenue: { function: 'sum', field: 'total' },
|
|
512
|
+
orders: { function: 'count', field: 'id' },
|
|
513
|
+
},
|
|
514
|
+
groupBy: 'category',
|
|
515
|
+
filter: { status: { eq: 'completed' } },
|
|
516
|
+
dateRange: {
|
|
517
|
+
start: '2025-01-01',
|
|
518
|
+
end: '2025-12-31',
|
|
519
|
+
},
|
|
520
|
+
});
|
|
521
|
+
|
|
522
|
+
// Quick count
|
|
523
|
+
const activeUsers = await baasix.reports.count('users', {
|
|
524
|
+
status: { eq: 'active' },
|
|
525
|
+
});
|
|
526
|
+
```
|
|
527
|
+
|
|
528
|
+
## Workflows
|
|
529
|
+
|
|
530
|
+
```typescript
|
|
531
|
+
// Execute workflow
|
|
532
|
+
const result = await baasix.workflows.execute('workflow-uuid', {
|
|
533
|
+
orderId: 'order-123',
|
|
534
|
+
});
|
|
535
|
+
|
|
536
|
+
// Get execution history
|
|
537
|
+
const { data: executions } = await baasix.workflows.getExecutions('workflow-uuid');
|
|
538
|
+
|
|
539
|
+
// Subscribe to execution updates (requires realtime)
|
|
540
|
+
const unsubscribe = baasix.realtime.subscribeToExecution(executionId, (update) => {
|
|
541
|
+
console.log('Execution progress:', update.progress, '%');
|
|
542
|
+
if (update.status === 'complete') {
|
|
543
|
+
console.log('Workflow finished!', update.result);
|
|
544
|
+
}
|
|
545
|
+
});
|
|
546
|
+
```
|
|
547
|
+
|
|
548
|
+
## Realtime Subscriptions
|
|
549
|
+
|
|
550
|
+
The SDK supports real-time data updates via WebSocket connections.
|
|
551
|
+
|
|
552
|
+
### Setup
|
|
553
|
+
|
|
554
|
+
```typescript
|
|
555
|
+
// Install socket.io-client separately
|
|
556
|
+
npm install socket.io-client
|
|
557
|
+
|
|
558
|
+
// Initialize realtime
|
|
559
|
+
import { io } from 'socket.io-client';
|
|
560
|
+
|
|
561
|
+
// Set the socket client
|
|
562
|
+
baasix.realtime.setSocketClient(io);
|
|
563
|
+
|
|
564
|
+
// Connect to realtime server
|
|
565
|
+
await baasix.realtime.connect();
|
|
566
|
+
```
|
|
567
|
+
|
|
568
|
+
### Subscribe to Collections
|
|
569
|
+
|
|
570
|
+
```typescript
|
|
571
|
+
// Subscribe to all changes on a collection
|
|
572
|
+
const unsubscribe = baasix.realtime.subscribe('products', (payload) => {
|
|
573
|
+
console.log(`Product ${payload.action}:`, payload.data);
|
|
574
|
+
// payload.action: 'create' | 'update' | 'delete'
|
|
575
|
+
// payload.data: the created/updated/deleted item
|
|
576
|
+
// payload.timestamp: ISO timestamp
|
|
577
|
+
});
|
|
578
|
+
|
|
579
|
+
// Subscribe to specific events only
|
|
580
|
+
const unsubscribe = baasix.realtime.on('orders', 'create', (data) => {
|
|
581
|
+
console.log('New order received:', data);
|
|
582
|
+
});
|
|
583
|
+
|
|
584
|
+
// Unsubscribe when done
|
|
585
|
+
unsubscribe();
|
|
586
|
+
```
|
|
587
|
+
|
|
588
|
+
### Supabase-style Channel API
|
|
589
|
+
|
|
590
|
+
```typescript
|
|
591
|
+
const channel = baasix.realtime
|
|
592
|
+
.channel('products')
|
|
593
|
+
.on('INSERT', (payload) => console.log('New:', payload))
|
|
594
|
+
.on('UPDATE', (payload) => console.log('Updated:', payload))
|
|
595
|
+
.on('DELETE', (payload) => console.log('Deleted:', payload))
|
|
596
|
+
.subscribe();
|
|
597
|
+
|
|
598
|
+
// Later
|
|
599
|
+
channel.unsubscribe();
|
|
600
|
+
```
|
|
601
|
+
|
|
602
|
+
### Connection Management
|
|
603
|
+
|
|
604
|
+
```typescript
|
|
605
|
+
// Check connection status
|
|
606
|
+
if (baasix.realtime.isConnected) {
|
|
607
|
+
console.log('Connected to realtime server');
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
// Listen for connection changes
|
|
611
|
+
baasix.realtime.onConnectionChange((connected) => {
|
|
612
|
+
console.log('Realtime:', connected ? 'online' : 'offline');
|
|
613
|
+
});
|
|
614
|
+
|
|
615
|
+
// Disconnect
|
|
616
|
+
baasix.realtime.disconnect();
|
|
617
|
+
```
|
|
618
|
+
|
|
619
|
+
## OAuth / Social Login
|
|
620
|
+
|
|
621
|
+
```typescript
|
|
622
|
+
// Redirect to OAuth provider
|
|
623
|
+
const url = baasix.auth.getOAuthUrl({
|
|
624
|
+
provider: 'google', // 'google' | 'facebook' | 'apple' | 'github'
|
|
625
|
+
redirectUrl: 'https://myapp.com/auth/callback',
|
|
626
|
+
});
|
|
627
|
+
window.location.href = url;
|
|
628
|
+
|
|
629
|
+
// In your callback page
|
|
630
|
+
const params = new URLSearchParams(window.location.search);
|
|
631
|
+
const token = params.get('token');
|
|
632
|
+
|
|
633
|
+
if (token) {
|
|
634
|
+
const { user } = await baasix.auth.handleOAuthCallback(token);
|
|
635
|
+
console.log('Logged in as:', user.email);
|
|
636
|
+
}
|
|
637
|
+
```
|
|
638
|
+
|
|
639
|
+
## Invitation System (Multi-tenant)
|
|
640
|
+
|
|
641
|
+
```typescript
|
|
642
|
+
// Send invitation
|
|
643
|
+
await baasix.auth.sendInvite({
|
|
644
|
+
email: 'newuser@example.com',
|
|
645
|
+
roleId: 'editor-role-uuid',
|
|
646
|
+
tenantId: 'tenant-uuid',
|
|
647
|
+
redirectUrl: 'https://myapp.com/accept-invite',
|
|
648
|
+
});
|
|
649
|
+
|
|
650
|
+
// Verify invitation token (in callback page)
|
|
651
|
+
const result = await baasix.auth.verifyInvite(token);
|
|
652
|
+
if (result.valid) {
|
|
653
|
+
// Show registration form with pre-filled email
|
|
654
|
+
console.log('Invite for:', result.email);
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
// Register with invitation
|
|
658
|
+
const { user } = await baasix.auth.registerWithInvite({
|
|
659
|
+
email: 'newuser@example.com',
|
|
660
|
+
password: 'password123',
|
|
661
|
+
firstName: 'John',
|
|
662
|
+
lastName: 'Doe',
|
|
663
|
+
inviteToken: token,
|
|
664
|
+
});
|
|
665
|
+
```
|
|
666
|
+
|
|
667
|
+
## Users Management (Admin)
|
|
668
|
+
|
|
669
|
+
```typescript
|
|
670
|
+
// List users
|
|
671
|
+
const { data: users } = await baasix.users.find({
|
|
672
|
+
filter: { status: { eq: 'active' } },
|
|
673
|
+
limit: 20,
|
|
674
|
+
});
|
|
675
|
+
|
|
676
|
+
// Create user
|
|
677
|
+
const userId = await baasix.users.create({
|
|
678
|
+
email: 'user@example.com',
|
|
679
|
+
password: 'password123',
|
|
680
|
+
firstName: 'John',
|
|
681
|
+
role_Id: 'role-uuid',
|
|
682
|
+
});
|
|
683
|
+
|
|
684
|
+
// Update user
|
|
685
|
+
await baasix.users.update(userId, { firstName: 'Jane' });
|
|
686
|
+
|
|
687
|
+
// Admin password change
|
|
688
|
+
await baasix.users.changePassword(userId, 'newPassword123');
|
|
689
|
+
|
|
690
|
+
// Suspend/Activate user
|
|
691
|
+
await baasix.users.suspend(userId);
|
|
692
|
+
await baasix.users.activate(userId);
|
|
693
|
+
```
|
|
694
|
+
|
|
695
|
+
## Roles Management
|
|
696
|
+
|
|
697
|
+
```typescript
|
|
698
|
+
// List roles
|
|
699
|
+
const { data: roles } = await baasix.roles.find();
|
|
700
|
+
|
|
701
|
+
// Find by name
|
|
702
|
+
const adminRole = await baasix.roles.findByName('Administrator');
|
|
703
|
+
|
|
704
|
+
// Create role
|
|
705
|
+
const roleId = await baasix.roles.create({
|
|
706
|
+
name: 'Editor',
|
|
707
|
+
description: 'Content editors',
|
|
708
|
+
appAccess: true,
|
|
709
|
+
});
|
|
710
|
+
|
|
711
|
+
// Update role
|
|
712
|
+
await baasix.roles.update(roleId, { description: 'Updated description' });
|
|
713
|
+
```
|
|
714
|
+
|
|
715
|
+
## CSV/JSON Import
|
|
716
|
+
|
|
717
|
+
```typescript
|
|
718
|
+
// Import from CSV file
|
|
719
|
+
const result = await baasix.items('products').importCSV(csvFile, {
|
|
720
|
+
delimiter: ',',
|
|
721
|
+
skipFirstRow: true,
|
|
722
|
+
});
|
|
723
|
+
console.log(`Created: ${result.created}, Errors: ${result.errors.length}`);
|
|
724
|
+
|
|
725
|
+
// Import from JSON file
|
|
726
|
+
const result = await baasix.items('products').importJSON(jsonFile);
|
|
727
|
+
|
|
728
|
+
// Import from data array
|
|
729
|
+
const result = await baasix.items('products').importData([
|
|
730
|
+
{ name: 'Product 1', price: 29.99 },
|
|
731
|
+
{ name: 'Product 2', price: 39.99 },
|
|
732
|
+
]);
|
|
733
|
+
```
|
|
734
|
+
|
|
735
|
+
## Migrations (Admin)
|
|
736
|
+
|
|
737
|
+
```typescript
|
|
738
|
+
// Check migration status
|
|
739
|
+
const status = await baasix.migrations.status();
|
|
740
|
+
console.log(`Pending: ${status.pendingCount}`);
|
|
741
|
+
|
|
742
|
+
// Run pending migrations
|
|
743
|
+
if (status.hasPending) {
|
|
744
|
+
const result = await baasix.migrations.run();
|
|
745
|
+
console.log(`Ran ${result.migrationsRun.length} migrations`);
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
// Rollback last batch
|
|
749
|
+
const rollback = await baasix.migrations.rollbackLast();
|
|
750
|
+
```
|
|
751
|
+
|
|
752
|
+
## Notifications
|
|
753
|
+
|
|
754
|
+
```typescript
|
|
755
|
+
// Get notifications
|
|
756
|
+
const { data } = await baasix.notifications.find({
|
|
757
|
+
limit: 20,
|
|
758
|
+
seen: false,
|
|
759
|
+
});
|
|
760
|
+
|
|
761
|
+
// Mark as seen
|
|
762
|
+
await baasix.notifications.markAsSeen('notification-uuid');
|
|
763
|
+
await baasix.notifications.markAllSeen();
|
|
764
|
+
|
|
765
|
+
// Get unread count
|
|
766
|
+
const count = await baasix.notifications.getUnreadCount();
|
|
767
|
+
```
|
|
768
|
+
|
|
769
|
+
## Custom Storage Adapter
|
|
770
|
+
|
|
771
|
+
Create a custom storage adapter for any environment:
|
|
772
|
+
|
|
773
|
+
```typescript
|
|
774
|
+
import { StorageAdapter } from '@tspvivek/baasix-sdk';
|
|
775
|
+
|
|
776
|
+
class MyCustomStorage implements StorageAdapter {
|
|
777
|
+
async get(key: string): Promise<string | null> {
|
|
778
|
+
// Your implementation
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
async set(key: string, value: string): Promise<void> {
|
|
782
|
+
// Your implementation
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
async remove(key: string): Promise<void> {
|
|
786
|
+
// Your implementation
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
async clear(): Promise<void> {
|
|
790
|
+
// Your implementation
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
const baasix = createBaasix({
|
|
795
|
+
url: 'https://api.example.com',
|
|
796
|
+
storage: new MyCustomStorage(),
|
|
797
|
+
});
|
|
798
|
+
```
|
|
799
|
+
|
|
800
|
+
## Error Handling
|
|
801
|
+
|
|
802
|
+
```typescript
|
|
803
|
+
import { BaasixError } from '@tspvivek/baasix-sdk';
|
|
804
|
+
|
|
805
|
+
try {
|
|
806
|
+
await baasix.items('products').create({ name: 'Product' });
|
|
807
|
+
} catch (error) {
|
|
808
|
+
if (error instanceof BaasixError) {
|
|
809
|
+
console.error('Status:', error.status);
|
|
810
|
+
console.error('Code:', error.code);
|
|
811
|
+
console.error('Message:', error.message);
|
|
812
|
+
console.error('Details:', error.details);
|
|
813
|
+
|
|
814
|
+
if (error.status === 401) {
|
|
815
|
+
// Handle unauthorized
|
|
816
|
+
}
|
|
817
|
+
if (error.status === 403) {
|
|
818
|
+
// Handle forbidden
|
|
819
|
+
}
|
|
820
|
+
if (error.isRetryable) {
|
|
821
|
+
// Can retry request
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
```
|
|
826
|
+
|
|
827
|
+
## TypeScript Support
|
|
828
|
+
|
|
829
|
+
Use generics for type-safe operations:
|
|
830
|
+
|
|
831
|
+
```typescript
|
|
832
|
+
interface Product {
|
|
833
|
+
id: string;
|
|
834
|
+
name: string;
|
|
835
|
+
price: number;
|
|
836
|
+
category_Id: string;
|
|
837
|
+
createdAt: string;
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
// Typed items module
|
|
841
|
+
const products = baasix.items<Product>('products');
|
|
842
|
+
|
|
843
|
+
// Type inference
|
|
844
|
+
const { data } = await products.find();
|
|
845
|
+
// data is Product[]
|
|
846
|
+
|
|
847
|
+
const product = await products.findOne('uuid');
|
|
848
|
+
// product is Product
|
|
849
|
+
|
|
850
|
+
await products.create({
|
|
851
|
+
name: 'Widget',
|
|
852
|
+
price: 29.99,
|
|
853
|
+
category_Id: 'cat-uuid',
|
|
854
|
+
}); // Type-checked
|
|
855
|
+
```
|
|
856
|
+
|
|
857
|
+
## React Example
|
|
858
|
+
|
|
859
|
+
```tsx
|
|
860
|
+
import { createBaasix } from '@tspvivek/baasix-sdk';
|
|
861
|
+
import { useEffect, useState } from 'react';
|
|
862
|
+
|
|
863
|
+
const baasix = createBaasix({
|
|
864
|
+
url: process.env.REACT_APP_BAASIX_URL!,
|
|
865
|
+
onAuthStateChange: (event, user) => {
|
|
866
|
+
console.log('Auth:', event, user);
|
|
867
|
+
},
|
|
868
|
+
});
|
|
869
|
+
|
|
870
|
+
function App() {
|
|
871
|
+
const [user, setUser] = useState(null);
|
|
872
|
+
const [products, setProducts] = useState([]);
|
|
873
|
+
|
|
874
|
+
useEffect(() => {
|
|
875
|
+
// Initialize auth on mount
|
|
876
|
+
baasix.auth.initialize().then((state) => {
|
|
877
|
+
setUser(state.user);
|
|
878
|
+
});
|
|
879
|
+
}, []);
|
|
880
|
+
|
|
881
|
+
useEffect(() => {
|
|
882
|
+
// Fetch products
|
|
883
|
+
baasix.items('products')
|
|
884
|
+
.find({ filter: { status: { eq: 'active' } } })
|
|
885
|
+
.then(({ data }) => setProducts(data));
|
|
886
|
+
}, []);
|
|
887
|
+
|
|
888
|
+
const handleLogin = async (email, password) => {
|
|
889
|
+
const { user } = await baasix.auth.login({ email, password });
|
|
890
|
+
setUser(user);
|
|
891
|
+
};
|
|
892
|
+
|
|
893
|
+
const handleLogout = async () => {
|
|
894
|
+
await baasix.auth.logout();
|
|
895
|
+
setUser(null);
|
|
896
|
+
};
|
|
897
|
+
|
|
898
|
+
return (
|
|
899
|
+
<div>
|
|
900
|
+
{user ? (
|
|
901
|
+
<>
|
|
902
|
+
<p>Welcome, {user.email}</p>
|
|
903
|
+
<button onClick={handleLogout}>Logout</button>
|
|
904
|
+
</>
|
|
905
|
+
) : (
|
|
906
|
+
<LoginForm onSubmit={handleLogin} />
|
|
907
|
+
)}
|
|
908
|
+
<ProductList products={products} />
|
|
909
|
+
</div>
|
|
910
|
+
);
|
|
911
|
+
}
|
|
912
|
+
```
|
|
913
|
+
|
|
914
|
+
## API Reference
|
|
915
|
+
|
|
916
|
+
### `createBaasix(config)`
|
|
917
|
+
|
|
918
|
+
Creates a new Baasix SDK instance.
|
|
919
|
+
|
|
920
|
+
| Option | Type | Default | Description |
|
|
921
|
+
|--------|------|---------|-------------|
|
|
922
|
+
| `url` | `string` | Required | Your Baasix server URL |
|
|
923
|
+
| `authMode` | `'jwt' \| 'cookie'` | `'jwt'` | Authentication mode |
|
|
924
|
+
| `storage` | `StorageAdapter` | Auto-detected | Token storage adapter |
|
|
925
|
+
| `token` | `string` | - | Static auth token |
|
|
926
|
+
| `timeout` | `number` | `30000` | Request timeout (ms) |
|
|
927
|
+
| `autoRefresh` | `boolean` | `true` | Auto-refresh tokens |
|
|
928
|
+
| `headers` | `object` | `{}` | Custom headers |
|
|
929
|
+
| `tenantId` | `string` | - | Multi-tenant ID |
|
|
930
|
+
| `credentials` | `RequestCredentials` | Based on authMode | Fetch credentials |
|
|
931
|
+
| `onAuthStateChange` | `function` | - | Auth state callback |
|
|
932
|
+
| `onError` | `function` | - | Global error handler |
|
|
933
|
+
|
|
934
|
+
### Storage Adapters
|
|
935
|
+
|
|
936
|
+
- `LocalStorageAdapter` - Browser localStorage (default for web)
|
|
937
|
+
- `MemoryStorageAdapter` - In-memory (default for SSR/Node.js)
|
|
938
|
+
- `AsyncStorageAdapter` - React Native AsyncStorage
|
|
939
|
+
|
|
940
|
+
## License
|
|
941
|
+
|
|
942
|
+
MIT © [Vivek Palanisamy](https://www.baasix.com)
|