@taruvi/sdk 1.1.6 → 1.1.8
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 +1086 -461
- package/package.json +1 -1
- package/src/lib/Database/DatabaseClient.ts +9 -5
- package/src/lib/Storage/StorageClient.ts +1 -1
- package/src/lib-internal/http/HttpClient.ts +2 -2
- package/src/lib-internal/http/types.ts +9 -7
- package/src/lib-internal/routes/UserRoutes.ts +0 -2
- package/src/utils/utils.ts +3 -3
- package/AI_DOCS.md +0 -1107
- package/AI_IMPLEMENTATION_GUIDE.md +0 -1239
- package/DATAPROVIDER_CONTEXT.md +0 -828
package/README.md
CHANGED
|
@@ -1,703 +1,1328 @@
|
|
|
1
|
-
# Taruvi SDK
|
|
1
|
+
# Taruvi SDK - AI Implementation Guide
|
|
2
2
|
|
|
3
|
-
>
|
|
3
|
+
> Quick reference for implementing Taruvi SDK features with copy-paste examples from production code
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
## Installation
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
```bash
|
|
8
|
+
npm install @taruvi/sdk
|
|
9
|
+
```
|
|
8
10
|
|
|
9
|
-
|
|
11
|
+
## What's New
|
|
10
12
|
|
|
11
|
-
|
|
13
|
+
Recent updates to the SDK:
|
|
12
14
|
|
|
13
|
-
- **
|
|
14
|
-
- **
|
|
15
|
-
- **
|
|
16
|
-
- **
|
|
17
|
-
- **
|
|
18
|
-
- **
|
|
19
|
-
- **
|
|
15
|
+
- **Analytics Service**: New service for executing analytics queries with `execute()` method. Pass query name and parameters to run predefined analytics.
|
|
16
|
+
- **Policy Service**: New service for checking resource-level permissions with `checkResource()` method. Supports batch resource checking with entity types, tables, and attributes.
|
|
17
|
+
- **App Service**: New service to retrieve app roles with `roles()` method.
|
|
18
|
+
- **User Types**: Added `UserCreateRequest`, `UserResponse`, and `UserDataResponse` types for user management.
|
|
19
|
+
- **Auth Service**: Web UI Flow with `login()`, `signup()`, `logout()` methods that redirect to backend pages. Token refresh with rotation support, `getCurrentUser()` for JWT decoding.
|
|
20
|
+
- **Database Service**: Added `create()` method for creating records. Added `populate()` method for eager loading related records. Comprehensive filter support with Django-style operators (`__gte`, `__lte`, `__icontains`, etc.).
|
|
21
|
+
- **Storage Service**: Added `download()` method. Enhanced filter support with size, date, MIME type, visibility filters. `delete()` now accepts array of paths for bulk deletion.
|
|
22
|
+
- **Client**: Automatic token extraction from URL hash after OAuth callback - no manual token handling needed.
|
|
23
|
+
- **Types**: Comprehensive `StorageFilters` and `DatabaseFilters` interfaces with full operator support.
|
|
20
24
|
|
|
21
|
-
|
|
25
|
+
## Core Setup
|
|
22
26
|
|
|
23
|
-
|
|
27
|
+
### 1. Initialize Client
|
|
24
28
|
|
|
25
|
-
```
|
|
26
|
-
|
|
27
|
-
# or
|
|
28
|
-
yarn add @taruvi/sdk
|
|
29
|
-
# or
|
|
30
|
-
pnpm add @taruvi/sdk
|
|
31
|
-
```
|
|
29
|
+
```typescript
|
|
30
|
+
import { Client } from '@taruvi/sdk'
|
|
32
31
|
|
|
33
|
-
|
|
32
|
+
const taruviClient = new Client({
|
|
33
|
+
apiKey: "your-site-api-key",
|
|
34
|
+
appSlug: "your-app-slug",
|
|
35
|
+
baseUrl: "https://taruvi-site.taruvi.cloud"
|
|
36
|
+
})
|
|
37
|
+
```
|
|
34
38
|
|
|
35
|
-
|
|
39
|
+
### 2. Pass to Components (React)
|
|
36
40
|
|
|
37
41
|
```typescript
|
|
38
|
-
|
|
42
|
+
// App.tsx
|
|
43
|
+
<Route path="/page" element={<MyPage taruviClient={taruviClient} />} />
|
|
39
44
|
|
|
40
|
-
//
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
baseUrl: 'https://test-api.taruvi.cloud'
|
|
45
|
-
})
|
|
45
|
+
// MyPage.tsx
|
|
46
|
+
interface MyPageProps {
|
|
47
|
+
taruviClient: Client;
|
|
48
|
+
}
|
|
46
49
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
+
export default function MyPage({ taruviClient }: MyPageProps) {
|
|
51
|
+
// Use SDK here
|
|
52
|
+
}
|
|
53
|
+
```
|
|
50
54
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
+
### 3. Automatic Token Handling
|
|
56
|
+
|
|
57
|
+
The Client automatically extracts authentication tokens from URL hash after OAuth callback:
|
|
58
|
+
|
|
59
|
+
```typescript
|
|
60
|
+
// After login redirect, URL contains:
|
|
61
|
+
// #session_token=xxx&access_token=yyy&refresh_token=zzz&expires_in=172800&token_type=Bearer
|
|
62
|
+
|
|
63
|
+
// Client automatically:
|
|
64
|
+
// 1. Extracts tokens from URL hash
|
|
65
|
+
// 2. Stores them in TokenClient
|
|
66
|
+
// 3. Clears the hash from URL
|
|
67
|
+
|
|
68
|
+
const taruviClient = new Client({ apiKey, appSlug, baseUrl })
|
|
69
|
+
// Tokens are now available automatically if present in URL hash
|
|
55
70
|
```
|
|
56
71
|
|
|
57
72
|
---
|
|
58
73
|
|
|
59
|
-
##
|
|
74
|
+
## Auth Service
|
|
60
75
|
|
|
61
|
-
###
|
|
62
|
-
|
|
63
|
-
Taruvi SDK uses dependency injection instead of singletons, allowing multiple client instances and better testability.
|
|
76
|
+
### Check Authentication
|
|
64
77
|
|
|
65
78
|
```typescript
|
|
66
|
-
|
|
67
|
-
const prodClient = new Client({ apiKey: '...', appSlug: 'prod', baseUrl: '...' })
|
|
68
|
-
const devClient = new Client({ apiKey: '...', appSlug: 'dev', baseUrl: '...' })
|
|
79
|
+
import { Auth } from '@taruvi/sdk'
|
|
69
80
|
|
|
70
|
-
|
|
71
|
-
const
|
|
72
|
-
const devUser = new User(devClient)
|
|
81
|
+
const auth = new Auth(taruviClient)
|
|
82
|
+
const isAuthenticated = auth.isUserAuthenticated() // Returns boolean (synchronous)
|
|
73
83
|
```
|
|
74
84
|
|
|
75
|
-
###
|
|
76
|
-
|
|
77
|
-
Services are manually instantiated, allowing tree-shaking to eliminate unused code.
|
|
85
|
+
### Login Flow (Web UI Flow with Redirect)
|
|
78
86
|
|
|
79
87
|
```typescript
|
|
80
|
-
|
|
81
|
-
import {
|
|
88
|
+
import { useEffect } from "react"
|
|
89
|
+
import { Auth } from '@taruvi/sdk'
|
|
90
|
+
|
|
91
|
+
export default function Login({ taruviClient }) {
|
|
92
|
+
const auth = new Auth(taruviClient)
|
|
93
|
+
|
|
94
|
+
useEffect(() => {
|
|
95
|
+
const isAuthenticated = auth.isUserAuthenticated()
|
|
96
|
+
|
|
97
|
+
if (isAuthenticated) {
|
|
98
|
+
window.location.href = "/dashboard"
|
|
99
|
+
} else {
|
|
100
|
+
// Redirects to backend login page, then returns with tokens in URL hash
|
|
101
|
+
auth.login() // Optional: pass callback URL
|
|
102
|
+
}
|
|
103
|
+
}, [])
|
|
82
104
|
|
|
83
|
-
|
|
84
|
-
|
|
105
|
+
return <div>Checking authentication...</div>
|
|
106
|
+
}
|
|
85
107
|
```
|
|
86
108
|
|
|
87
|
-
###
|
|
109
|
+
### Signup Flow
|
|
110
|
+
|
|
111
|
+
```typescript
|
|
112
|
+
import { Auth } from '@taruvi/sdk'
|
|
113
|
+
|
|
114
|
+
const auth = new Auth(taruviClient)
|
|
88
115
|
|
|
89
|
-
|
|
116
|
+
// Redirect to signup page (Web UI Flow)
|
|
117
|
+
auth.signup() // Optional: pass callback URL
|
|
118
|
+
auth.signup("/dashboard") // Redirect to dashboard after signup
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### Logout
|
|
90
122
|
|
|
91
123
|
```typescript
|
|
92
|
-
|
|
93
|
-
apiKey: 'site_abc123', // Same site
|
|
94
|
-
appSlug: 'customer-portal', // Customer data
|
|
95
|
-
baseUrl: 'https://test-api.taruvi.cloud'
|
|
96
|
-
})
|
|
124
|
+
import { Auth } from '@taruvi/sdk'
|
|
97
125
|
|
|
98
|
-
const
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
126
|
+
const auth = new Auth(taruviClient)
|
|
127
|
+
|
|
128
|
+
// Clear tokens and redirect to logout page
|
|
129
|
+
await auth.logout() // Optional: pass callback URL
|
|
130
|
+
await auth.logout("/") // Redirect to home after logout
|
|
103
131
|
```
|
|
104
132
|
|
|
105
|
-
|
|
133
|
+
### Get Current User
|
|
106
134
|
|
|
107
|
-
|
|
135
|
+
```typescript
|
|
136
|
+
import { Auth } from '@taruvi/sdk'
|
|
108
137
|
|
|
109
|
-
|
|
138
|
+
const auth = new Auth(taruviClient)
|
|
110
139
|
|
|
111
|
-
|
|
140
|
+
// Get user info from decoded JWT access token
|
|
141
|
+
const user = auth.getCurrentUser()
|
|
142
|
+
console.log(user?.user_id)
|
|
143
|
+
console.log(user?.username)
|
|
144
|
+
console.log(user?.email)
|
|
145
|
+
```
|
|
112
146
|
|
|
113
|
-
|
|
147
|
+
### Token Management
|
|
114
148
|
|
|
115
149
|
```typescript
|
|
116
|
-
import {
|
|
150
|
+
import { Auth } from '@taruvi/sdk'
|
|
117
151
|
|
|
118
|
-
const
|
|
119
|
-
const auth = new Auth(client)
|
|
152
|
+
const auth = new Auth(taruviClient)
|
|
120
153
|
|
|
121
|
-
//
|
|
122
|
-
|
|
154
|
+
// Get tokens
|
|
155
|
+
const accessToken = auth.getAccessToken()
|
|
156
|
+
const refreshToken = auth.getRefreshToken()
|
|
123
157
|
|
|
124
|
-
// Check
|
|
125
|
-
const
|
|
126
|
-
```
|
|
158
|
+
// Check if token is expired
|
|
159
|
+
const isExpired = auth.isTokenExpired()
|
|
127
160
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
161
|
+
// Refresh access token (returns new access AND refresh tokens due to rotation)
|
|
162
|
+
const newTokens = await auth.refreshAccessToken()
|
|
163
|
+
if (newTokens) {
|
|
164
|
+
console.log(newTokens.access)
|
|
165
|
+
console.log(newTokens.refresh)
|
|
166
|
+
console.log(newTokens.expires_in)
|
|
167
|
+
}
|
|
168
|
+
```
|
|
134
169
|
|
|
135
170
|
---
|
|
136
171
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
User profile and management operations.
|
|
172
|
+
## User Service
|
|
140
173
|
|
|
141
|
-
|
|
174
|
+
### Get Current User
|
|
142
175
|
|
|
143
176
|
```typescript
|
|
144
|
-
import {
|
|
145
|
-
|
|
146
|
-
const client = new Client({ /* config */ })
|
|
147
|
-
const user = new User(client)
|
|
177
|
+
import { User } from '@taruvi/sdk'
|
|
148
178
|
|
|
149
|
-
|
|
179
|
+
const user = new User(taruviClient)
|
|
150
180
|
const userData = await user.getUserData()
|
|
151
181
|
|
|
152
|
-
|
|
182
|
+
console.log(userData.data.username)
|
|
183
|
+
console.log(userData.data.email)
|
|
184
|
+
console.log(userData.data.full_name)
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
### Create User (Registration)
|
|
188
|
+
|
|
189
|
+
```typescript
|
|
190
|
+
import { User } from '@taruvi/sdk'
|
|
191
|
+
|
|
192
|
+
const user = new User(taruviClient)
|
|
193
|
+
|
|
153
194
|
const newUser = await user.createUser({
|
|
154
|
-
username:
|
|
155
|
-
email:
|
|
156
|
-
password:
|
|
195
|
+
username: "john_doe",
|
|
196
|
+
email: "john@example.com",
|
|
197
|
+
password: "secure123",
|
|
198
|
+
confirm_password: "secure123",
|
|
199
|
+
first_name: "John",
|
|
200
|
+
last_name: "Doe",
|
|
201
|
+
is_active: true,
|
|
202
|
+
is_staff: false,
|
|
203
|
+
attributes: ""
|
|
157
204
|
})
|
|
205
|
+
```
|
|
158
206
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
207
|
+
### Update User
|
|
208
|
+
|
|
209
|
+
```typescript
|
|
210
|
+
const user = new User(taruviClient)
|
|
163
211
|
|
|
164
|
-
|
|
165
|
-
|
|
212
|
+
await user.updateUser("john_doe", {
|
|
213
|
+
email: "newemail@example.com",
|
|
214
|
+
first_name: "Johnny"
|
|
215
|
+
})
|
|
166
216
|
```
|
|
167
217
|
|
|
168
|
-
|
|
169
|
-
- ✅ `getUserData()` - Fetch current user details
|
|
170
|
-
- ✅ `createUser(data)` - Create new user account
|
|
171
|
-
- ✅ `updateUser(username, data)` - Update user information
|
|
172
|
-
- ✅ `deleteUser(username)` - Delete user account
|
|
218
|
+
### Delete User
|
|
173
219
|
|
|
174
|
-
|
|
220
|
+
```typescript
|
|
221
|
+
const user = new User(taruviClient)
|
|
222
|
+
await user.deleteUser("john_doe")
|
|
223
|
+
```
|
|
175
224
|
|
|
176
|
-
###
|
|
225
|
+
### List Users
|
|
177
226
|
|
|
178
|
-
|
|
227
|
+
```typescript
|
|
228
|
+
const user = new User(taruviClient)
|
|
229
|
+
|
|
230
|
+
const users = await user.list({
|
|
231
|
+
search: "john",
|
|
232
|
+
is_active: true,
|
|
233
|
+
is_staff: false,
|
|
234
|
+
is_superuser: false,
|
|
235
|
+
is_deleted: false,
|
|
236
|
+
ordering: "-date_joined",
|
|
237
|
+
page: 1,
|
|
238
|
+
page_size: 20
|
|
239
|
+
})
|
|
240
|
+
```
|
|
179
241
|
|
|
180
|
-
|
|
242
|
+
### Get User Apps
|
|
181
243
|
|
|
182
244
|
```typescript
|
|
183
|
-
|
|
245
|
+
const user = new User(taruviClient)
|
|
246
|
+
const apps = await user.getUserApps("john_doe")
|
|
184
247
|
|
|
185
|
-
|
|
186
|
-
|
|
248
|
+
// Returns array of apps the user has access to
|
|
249
|
+
apps.forEach(app => {
|
|
250
|
+
console.log(app.name, app.slug, app.url)
|
|
251
|
+
})
|
|
252
|
+
```
|
|
187
253
|
|
|
188
|
-
|
|
189
|
-
const allPosts = await storage
|
|
190
|
-
.from('posts')
|
|
191
|
-
.execute()
|
|
254
|
+
### Complete Registration Form Example
|
|
192
255
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
256
|
+
```typescript
|
|
257
|
+
import { useState } from "react"
|
|
258
|
+
import { User } from "@taruvi/sdk"
|
|
259
|
+
import { useNavigate } from "react-router"
|
|
260
|
+
|
|
261
|
+
export default function Register({ taruviClient }) {
|
|
262
|
+
const navigate = useNavigate()
|
|
263
|
+
const [formData, setFormData] = useState({
|
|
264
|
+
username: "",
|
|
265
|
+
email: "",
|
|
266
|
+
password: "",
|
|
267
|
+
confirm_password: "",
|
|
268
|
+
first_name: "",
|
|
269
|
+
last_name: "",
|
|
270
|
+
is_active: true,
|
|
271
|
+
is_staff: false
|
|
272
|
+
})
|
|
273
|
+
const [error, setError] = useState("")
|
|
274
|
+
const [loading, setLoading] = useState(false)
|
|
275
|
+
|
|
276
|
+
const handleSubmit = async (e) => {
|
|
277
|
+
e.preventDefault()
|
|
278
|
+
|
|
279
|
+
if (formData.password !== formData.confirm_password) {
|
|
280
|
+
setError("Passwords do not match")
|
|
281
|
+
return
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
setLoading(true)
|
|
285
|
+
try {
|
|
286
|
+
const userClient = new User(taruviClient)
|
|
287
|
+
await userClient.createUser(formData)
|
|
288
|
+
navigate("/login")
|
|
289
|
+
} catch (err) {
|
|
290
|
+
setError(err.message || "Failed to create user")
|
|
291
|
+
} finally {
|
|
292
|
+
setLoading(false)
|
|
293
|
+
}
|
|
294
|
+
}
|
|
199
295
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
296
|
+
return (
|
|
297
|
+
<form onSubmit={handleSubmit}>
|
|
298
|
+
<input
|
|
299
|
+
name="username"
|
|
300
|
+
value={formData.username}
|
|
301
|
+
onChange={(e) => setFormData({...formData, username: e.target.value})}
|
|
302
|
+
required
|
|
303
|
+
/>
|
|
304
|
+
{/* Add other fields */}
|
|
305
|
+
<button type="submit" disabled={loading}>
|
|
306
|
+
{loading ? "Creating..." : "Register"}
|
|
307
|
+
</button>
|
|
308
|
+
{error && <div>{error}</div>}
|
|
309
|
+
</form>
|
|
310
|
+
)
|
|
311
|
+
}
|
|
312
|
+
```
|
|
207
313
|
|
|
208
314
|
---
|
|
209
315
|
|
|
210
|
-
|
|
316
|
+
## Database Service (CRUD Operations)
|
|
211
317
|
|
|
212
|
-
|
|
318
|
+
### Fetch All Records
|
|
213
319
|
|
|
214
|
-
|
|
320
|
+
```typescript
|
|
321
|
+
import { Database } from '@taruvi/sdk'
|
|
322
|
+
|
|
323
|
+
const db = new Database(taruviClient)
|
|
324
|
+
const response = await db.from("accounts").execute()
|
|
325
|
+
|
|
326
|
+
if (response.data) {
|
|
327
|
+
console.log(response.data) // Array of records
|
|
328
|
+
}
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
### Fetch Single Record
|
|
215
332
|
|
|
216
333
|
```typescript
|
|
217
|
-
|
|
334
|
+
const db = new Database(taruviClient)
|
|
335
|
+
const record = await db.from("accounts").get("record-id").execute()
|
|
336
|
+
```
|
|
218
337
|
|
|
219
|
-
|
|
220
|
-
const settings = new Settings(client)
|
|
338
|
+
### Create Record
|
|
221
339
|
|
|
222
|
-
|
|
223
|
-
const
|
|
224
|
-
|
|
340
|
+
```typescript
|
|
341
|
+
const db = new Database(taruviClient)
|
|
342
|
+
await db.from("accounts").create({
|
|
343
|
+
name: "John Doe",
|
|
344
|
+
email: "john@example.com",
|
|
345
|
+
status: "active"
|
|
346
|
+
}).execute()
|
|
225
347
|
```
|
|
226
348
|
|
|
227
|
-
|
|
349
|
+
### Update Record
|
|
350
|
+
|
|
351
|
+
```typescript
|
|
352
|
+
const db = new Database(taruviClient)
|
|
353
|
+
await db.from("accounts").get("record-id").update({
|
|
354
|
+
name: "Updated Name",
|
|
355
|
+
status: "active"
|
|
356
|
+
}).execute()
|
|
357
|
+
```
|
|
228
358
|
|
|
229
|
-
###
|
|
359
|
+
### Delete Record
|
|
230
360
|
|
|
231
|
-
|
|
361
|
+
```typescript
|
|
362
|
+
const db = new Database(taruviClient)
|
|
363
|
+
await db.from("accounts").delete("record-id").execute()
|
|
364
|
+
```
|
|
232
365
|
|
|
233
|
-
|
|
366
|
+
### Filter Records
|
|
234
367
|
|
|
235
368
|
```typescript
|
|
236
|
-
|
|
369
|
+
const db = new Database(taruviClient)
|
|
370
|
+
|
|
371
|
+
// Simple field filters
|
|
372
|
+
const filtered = await db
|
|
373
|
+
.from("accounts")
|
|
374
|
+
.filter({
|
|
375
|
+
status: "active",
|
|
376
|
+
country: "USA"
|
|
377
|
+
})
|
|
378
|
+
.execute()
|
|
237
379
|
|
|
238
|
-
|
|
239
|
-
const
|
|
380
|
+
// Advanced filters with operators
|
|
381
|
+
const advanced = await db
|
|
382
|
+
.from("accounts")
|
|
383
|
+
.filter({
|
|
384
|
+
age__gte: 18, // age >= 18
|
|
385
|
+
age__lt: 65, // age < 65
|
|
386
|
+
name__icontains: "john", // case-insensitive contains
|
|
387
|
+
created_at__gte: "2024-01-01",
|
|
388
|
+
ordering: "-created_at" // Sort by created_at descending
|
|
389
|
+
})
|
|
390
|
+
.execute()
|
|
240
391
|
|
|
241
|
-
//
|
|
242
|
-
const
|
|
243
|
-
.from(
|
|
244
|
-
.
|
|
245
|
-
|
|
392
|
+
// Pagination
|
|
393
|
+
const paginated = await db
|
|
394
|
+
.from("accounts")
|
|
395
|
+
.filter({
|
|
396
|
+
page: 1,
|
|
397
|
+
pageSize: 20
|
|
398
|
+
})
|
|
246
399
|
.execute()
|
|
247
400
|
```
|
|
248
401
|
|
|
249
|
-
|
|
402
|
+
### Populate Related Records
|
|
250
403
|
|
|
251
|
-
|
|
404
|
+
Use `populate()` to eager load related records (foreign key relationships):
|
|
252
405
|
|
|
253
|
-
|
|
406
|
+
```typescript
|
|
407
|
+
const db = new Database(taruviClient)
|
|
408
|
+
|
|
409
|
+
// Populate a single relation
|
|
410
|
+
const orders = await db
|
|
411
|
+
.from("orders")
|
|
412
|
+
.populate(["customer"])
|
|
413
|
+
.execute()
|
|
414
|
+
|
|
415
|
+
// Each order now includes the full customer object instead of just the ID
|
|
416
|
+
console.log(orders.data[0].customer.name)
|
|
417
|
+
console.log(orders.data[0].customer.email)
|
|
418
|
+
|
|
419
|
+
// Populate multiple relations
|
|
420
|
+
const invoices = await db
|
|
421
|
+
.from("invoices")
|
|
422
|
+
.populate(["customer", "created_by", "items"])
|
|
423
|
+
.execute()
|
|
424
|
+
|
|
425
|
+
// Combine with filters
|
|
426
|
+
const recentOrders = await db
|
|
427
|
+
.from("orders")
|
|
428
|
+
.filter({
|
|
429
|
+
status: "completed",
|
|
430
|
+
created_at__gte: "2024-01-01",
|
|
431
|
+
ordering: "-created_at"
|
|
432
|
+
})
|
|
433
|
+
.populate(["customer", "product"])
|
|
434
|
+
.execute()
|
|
435
|
+
|
|
436
|
+
// Combine with pagination
|
|
437
|
+
const paginatedOrders = await db
|
|
438
|
+
.from("orders")
|
|
439
|
+
.filter({ page: 1, pageSize: 10 })
|
|
440
|
+
.populate(["customer"])
|
|
441
|
+
.execute()
|
|
442
|
+
```
|
|
254
443
|
|
|
255
|
-
|
|
444
|
+
### Complete CRUD Example (CRM Table)
|
|
256
445
|
|
|
257
446
|
```typescript
|
|
258
|
-
import {
|
|
447
|
+
import { useEffect, useState } from 'react'
|
|
448
|
+
import { Database } from '@taruvi/sdk'
|
|
449
|
+
|
|
450
|
+
export default function CrmTable({ taruviClient }) {
|
|
451
|
+
const [contacts, setContacts] = useState([])
|
|
452
|
+
|
|
453
|
+
const fetchContacts = async () => {
|
|
454
|
+
const db = new Database(taruviClient)
|
|
455
|
+
const response = await db.from("accounts").execute()
|
|
456
|
+
if (response.data) {
|
|
457
|
+
setContacts(response.data)
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
useEffect(() => {
|
|
462
|
+
fetchContacts()
|
|
463
|
+
}, [])
|
|
464
|
+
|
|
465
|
+
const handleCreate = async (data) => {
|
|
466
|
+
const db = new Database(taruviClient)
|
|
467
|
+
await db.from("accounts").create(data).execute()
|
|
468
|
+
fetchContacts() // Refresh
|
|
469
|
+
}
|
|
259
470
|
|
|
260
|
-
const
|
|
261
|
-
const
|
|
471
|
+
const handleDelete = async (id) => {
|
|
472
|
+
const db = new Database(taruviClient)
|
|
473
|
+
await db.from("accounts").delete(id).execute()
|
|
474
|
+
fetchContacts() // Refresh
|
|
475
|
+
}
|
|
262
476
|
|
|
263
|
-
|
|
264
|
-
const
|
|
477
|
+
const handleUpdate = async (id, data) => {
|
|
478
|
+
const db = new Database(taruviClient)
|
|
479
|
+
await db.from("accounts").get(id).update(data).execute()
|
|
480
|
+
fetchContacts() // Refresh
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
return (
|
|
484
|
+
<div>
|
|
485
|
+
<button onClick={() => handleCreate({ name: 'New Contact', status: 'active' })}>
|
|
486
|
+
Add Contact
|
|
487
|
+
</button>
|
|
488
|
+
<table>
|
|
489
|
+
{contacts.map(contact => (
|
|
490
|
+
<tr key={contact.id}>
|
|
491
|
+
<td>{contact.name}</td>
|
|
492
|
+
<td>
|
|
493
|
+
<button onClick={() => handleUpdate(contact.id, { status: 'updated' })}>
|
|
494
|
+
Edit
|
|
495
|
+
</button>
|
|
496
|
+
<button onClick={() => handleDelete(contact.id)}>
|
|
497
|
+
Delete
|
|
498
|
+
</button>
|
|
499
|
+
</td>
|
|
500
|
+
</tr>
|
|
501
|
+
))}
|
|
502
|
+
</table>
|
|
503
|
+
</div>
|
|
504
|
+
)
|
|
505
|
+
}
|
|
265
506
|
```
|
|
266
507
|
|
|
267
508
|
---
|
|
268
509
|
|
|
269
|
-
##
|
|
510
|
+
## Storage Service (File Management)
|
|
511
|
+
|
|
512
|
+
### List Files in Bucket
|
|
513
|
+
|
|
514
|
+
```typescript
|
|
515
|
+
import { Storage } from '@taruvi/sdk'
|
|
516
|
+
|
|
517
|
+
const storage = new Storage(taruviClient)
|
|
518
|
+
const files = await storage.from("documents").execute()
|
|
519
|
+
|
|
520
|
+
console.log(files.data) // Array of file objects
|
|
521
|
+
```
|
|
270
522
|
|
|
271
|
-
###
|
|
523
|
+
### Upload Files
|
|
272
524
|
|
|
273
525
|
```typescript
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
526
|
+
const storage = new Storage(taruviClient)
|
|
527
|
+
|
|
528
|
+
const filesData = {
|
|
529
|
+
files: [file1, file2], // File objects from input
|
|
530
|
+
metadatas: [{ name: "file1" }, { name: "file2" }],
|
|
531
|
+
paths: ["file1.pdf", "file2.pdf"]
|
|
279
532
|
}
|
|
533
|
+
|
|
534
|
+
await storage.from("documents").upload(filesData).execute()
|
|
280
535
|
```
|
|
281
536
|
|
|
282
|
-
###
|
|
537
|
+
### Download File
|
|
283
538
|
|
|
284
|
-
#### Basic Setup
|
|
285
539
|
```typescript
|
|
286
|
-
const
|
|
287
|
-
|
|
288
|
-
appSlug: 'my-app',
|
|
289
|
-
baseUrl: 'https://test-api.taruvi.cloud'
|
|
290
|
-
})
|
|
540
|
+
const storage = new Storage(taruviClient)
|
|
541
|
+
const file = await storage.from("documents").download("path/to/file.pdf").execute()
|
|
291
542
|
```
|
|
292
543
|
|
|
293
|
-
|
|
544
|
+
### Delete Files
|
|
545
|
+
|
|
294
546
|
```typescript
|
|
295
|
-
const
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
547
|
+
const storage = new Storage(taruviClient)
|
|
548
|
+
|
|
549
|
+
// Delete single file
|
|
550
|
+
await storage.from("documents").delete(["path/to/file.pdf"]).execute()
|
|
551
|
+
|
|
552
|
+
// Delete multiple files
|
|
553
|
+
await storage.from("documents").delete([
|
|
554
|
+
"path/to/file1.pdf",
|
|
555
|
+
"path/to/file2.pdf",
|
|
556
|
+
"path/to/file3.pdf"
|
|
557
|
+
]).execute()
|
|
301
558
|
```
|
|
302
559
|
|
|
303
|
-
|
|
560
|
+
### Update File Metadata
|
|
561
|
+
|
|
304
562
|
```typescript
|
|
305
|
-
const
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
563
|
+
const storage = new Storage(taruviClient)
|
|
564
|
+
await storage
|
|
565
|
+
.from("documents")
|
|
566
|
+
.update("path/to/file.pdf", {
|
|
567
|
+
filename: "newname.pdf",
|
|
568
|
+
visibility: "public",
|
|
569
|
+
metadata: { category: "reports" }
|
|
570
|
+
})
|
|
571
|
+
.execute()
|
|
572
|
+
```
|
|
573
|
+
|
|
574
|
+
### Filter Files
|
|
575
|
+
|
|
576
|
+
```typescript
|
|
577
|
+
const storage = new Storage(taruviClient)
|
|
578
|
+
|
|
579
|
+
// Basic filters
|
|
580
|
+
const filtered = await storage
|
|
581
|
+
.from("documents")
|
|
582
|
+
.filter({
|
|
583
|
+
search: "invoice",
|
|
584
|
+
visibility: "public",
|
|
585
|
+
mimetype_category: "document",
|
|
586
|
+
min_size: 1024,
|
|
587
|
+
ordering: "-created_at"
|
|
588
|
+
})
|
|
589
|
+
.execute()
|
|
590
|
+
|
|
591
|
+
// Advanced filters with operators
|
|
592
|
+
const advanced = await storage
|
|
593
|
+
.from("documents")
|
|
594
|
+
.filter({
|
|
595
|
+
// Size filters (bytes)
|
|
596
|
+
size__gte: 1024, // >= 1KB
|
|
597
|
+
size__lte: 10485760, // <= 10MB
|
|
598
|
+
|
|
599
|
+
// Date filters (ISO 8601)
|
|
600
|
+
created_at__gte: "2024-01-01",
|
|
601
|
+
created_at__lte: "2024-12-31",
|
|
602
|
+
|
|
603
|
+
// Search filters
|
|
604
|
+
filename__icontains: "report",
|
|
605
|
+
file__startswith: "invoice",
|
|
606
|
+
prefix: "uploads/2024/",
|
|
607
|
+
|
|
608
|
+
// MIME type filters
|
|
609
|
+
mimetype: "application/pdf",
|
|
610
|
+
mimetype_category: "image", // image, document, video, audio, etc.
|
|
611
|
+
|
|
612
|
+
// Visibility & user filters
|
|
613
|
+
visibility: "public",
|
|
614
|
+
created_by_me: true,
|
|
615
|
+
created_by__username: "john",
|
|
616
|
+
|
|
617
|
+
// Pagination & sorting
|
|
618
|
+
page: 1,
|
|
619
|
+
pageSize: 50,
|
|
620
|
+
ordering: "-created_at" // Sort by created_at descending
|
|
621
|
+
})
|
|
622
|
+
.execute()
|
|
623
|
+
```
|
|
624
|
+
|
|
625
|
+
### Complete File Upload Example
|
|
626
|
+
|
|
627
|
+
```typescript
|
|
628
|
+
import { useState, useRef } from 'react'
|
|
629
|
+
import { Storage } from '@taruvi/sdk'
|
|
630
|
+
|
|
631
|
+
export default function FileUploader({ taruviClient }) {
|
|
632
|
+
const [files, setFiles] = useState([])
|
|
633
|
+
const [uploading, setUploading] = useState(false)
|
|
634
|
+
const fileInputRef = useRef(null)
|
|
635
|
+
|
|
636
|
+
const handleFileSelect = (e) => {
|
|
637
|
+
const selectedFiles = Array.from(e.target.files)
|
|
638
|
+
setFiles(selectedFiles)
|
|
315
639
|
}
|
|
316
|
-
}
|
|
317
640
|
|
|
318
|
-
const
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
641
|
+
const uploadFiles = async () => {
|
|
642
|
+
if (files.length === 0) return
|
|
643
|
+
|
|
644
|
+
setUploading(true)
|
|
645
|
+
try {
|
|
646
|
+
const storage = new Storage(taruviClient)
|
|
647
|
+
|
|
648
|
+
const uploadData = {
|
|
649
|
+
files: files,
|
|
650
|
+
metadatas: files.map(f => ({ name: f.name })),
|
|
651
|
+
paths: files.map(f => f.name)
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
await storage.from("documents").upload(uploadData).execute()
|
|
655
|
+
|
|
656
|
+
alert("Upload successful!")
|
|
657
|
+
setFiles([])
|
|
658
|
+
} catch (error) {
|
|
659
|
+
alert("Upload failed: " + error.message)
|
|
660
|
+
} finally {
|
|
661
|
+
setUploading(false)
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
return (
|
|
666
|
+
<div>
|
|
667
|
+
<input
|
|
668
|
+
ref={fileInputRef}
|
|
669
|
+
type="file"
|
|
670
|
+
multiple
|
|
671
|
+
onChange={handleFileSelect}
|
|
672
|
+
/>
|
|
673
|
+
<button onClick={uploadFiles} disabled={uploading || files.length === 0}>
|
|
674
|
+
{uploading ? "Uploading..." : `Upload ${files.length} file(s)`}
|
|
675
|
+
</button>
|
|
676
|
+
</div>
|
|
677
|
+
)
|
|
678
|
+
}
|
|
323
679
|
```
|
|
324
680
|
|
|
325
681
|
---
|
|
326
682
|
|
|
327
|
-
##
|
|
683
|
+
## Functions Service (Serverless)
|
|
684
|
+
|
|
685
|
+
### Execute Function (Sync)
|
|
686
|
+
|
|
687
|
+
```typescript
|
|
688
|
+
import { Functions } from '@taruvi/sdk'
|
|
689
|
+
|
|
690
|
+
const functions = new Functions(taruviClient)
|
|
328
691
|
|
|
329
|
-
|
|
692
|
+
const result = await functions.execute("my-function", {
|
|
693
|
+
async: false,
|
|
694
|
+
params: {
|
|
695
|
+
key1: "value1",
|
|
696
|
+
key2: 123
|
|
697
|
+
}
|
|
698
|
+
})
|
|
699
|
+
|
|
700
|
+
console.log(result.data) // Function response
|
|
701
|
+
```
|
|
330
702
|
|
|
331
|
-
|
|
703
|
+
### Execute Function (Async)
|
|
332
704
|
|
|
333
705
|
```typescript
|
|
334
|
-
|
|
335
|
-
import { Client, User, Auth } from '@taruvi/sdk'
|
|
706
|
+
const functions = new Functions(taruviClient)
|
|
336
707
|
|
|
337
|
-
|
|
338
|
-
|
|
708
|
+
const result = await functions.execute("long-running-task", {
|
|
709
|
+
async: true,
|
|
710
|
+
params: { data: "value" }
|
|
711
|
+
})
|
|
339
712
|
|
|
340
|
-
//
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
713
|
+
console.log(result.invocation.invocation_id) // Track async execution
|
|
714
|
+
console.log(result.invocation.celery_task_id)
|
|
715
|
+
console.log(result.invocation.status)
|
|
716
|
+
```
|
|
717
|
+
|
|
718
|
+
### Complete Function Executor Example
|
|
719
|
+
|
|
720
|
+
```typescript
|
|
721
|
+
import { useState } from 'react'
|
|
722
|
+
import { Functions } from '@taruvi/sdk'
|
|
723
|
+
|
|
724
|
+
export default function FunctionExecutor({ taruviClient }) {
|
|
725
|
+
const [functionSlug, setFunctionSlug] = useState("")
|
|
726
|
+
const [params, setParams] = useState({})
|
|
727
|
+
const [isAsync, setIsAsync] = useState(false)
|
|
728
|
+
const [result, setResult] = useState(null)
|
|
729
|
+
const [loading, setLoading] = useState(false)
|
|
730
|
+
|
|
731
|
+
const executeFunction = async () => {
|
|
732
|
+
setLoading(true)
|
|
733
|
+
try {
|
|
734
|
+
const functions = new Functions(taruviClient)
|
|
735
|
+
const response = await functions.execute(functionSlug, {
|
|
736
|
+
async: isAsync,
|
|
737
|
+
params: params
|
|
738
|
+
})
|
|
739
|
+
setResult(response)
|
|
740
|
+
} catch (error) {
|
|
741
|
+
console.error(error)
|
|
742
|
+
} finally {
|
|
743
|
+
setLoading(false)
|
|
744
|
+
}
|
|
745
|
+
}
|
|
347
746
|
|
|
348
747
|
return (
|
|
349
|
-
<
|
|
350
|
-
|
|
351
|
-
|
|
748
|
+
<div>
|
|
749
|
+
<input
|
|
750
|
+
placeholder="Function slug"
|
|
751
|
+
value={functionSlug}
|
|
752
|
+
onChange={(e) => setFunctionSlug(e.target.value)}
|
|
753
|
+
/>
|
|
754
|
+
<label>
|
|
755
|
+
<input
|
|
756
|
+
type="checkbox"
|
|
757
|
+
checked={isAsync}
|
|
758
|
+
onChange={(e) => setIsAsync(e.target.checked)}
|
|
759
|
+
/>
|
|
760
|
+
Async
|
|
761
|
+
</label>
|
|
762
|
+
<button onClick={executeFunction} disabled={loading}>
|
|
763
|
+
Execute
|
|
764
|
+
</button>
|
|
765
|
+
{result && <pre>{JSON.stringify(result, null, 2)}</pre>}
|
|
766
|
+
</div>
|
|
352
767
|
)
|
|
353
768
|
}
|
|
769
|
+
```
|
|
354
770
|
|
|
355
|
-
|
|
356
|
-
export function useTaruvi() {
|
|
357
|
-
const client = useContext(TaruviContext)
|
|
358
|
-
if (!client) throw new Error('useTaruvi must be used within TaruviProvider')
|
|
359
|
-
return client
|
|
360
|
-
}
|
|
771
|
+
---
|
|
361
772
|
|
|
362
|
-
|
|
363
|
-
const client = useTaruvi()
|
|
364
|
-
return new Auth(client)
|
|
365
|
-
}
|
|
773
|
+
## Analytics Service
|
|
366
774
|
|
|
367
|
-
|
|
368
|
-
const client = useTaruvi()
|
|
369
|
-
return new User(client)
|
|
370
|
-
}
|
|
775
|
+
### Execute Analytics Query
|
|
371
776
|
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
const user = useUser()
|
|
375
|
-
const [data, setData] = useState(null)
|
|
777
|
+
```typescript
|
|
778
|
+
import { Analytics } from '@taruvi/sdk'
|
|
376
779
|
|
|
377
|
-
|
|
378
|
-
user.getUserData().then(setData)
|
|
379
|
-
}, [])
|
|
780
|
+
const analytics = new Analytics(taruviClient)
|
|
380
781
|
|
|
381
|
-
|
|
382
|
-
|
|
782
|
+
const result = await analytics.execute("monthly-sales-report", {
|
|
783
|
+
params: {
|
|
784
|
+
start_date: "2024-01-01",
|
|
785
|
+
end_date: "2024-12-31"
|
|
786
|
+
}
|
|
787
|
+
})
|
|
788
|
+
|
|
789
|
+
console.log(result.data) // Analytics query result
|
|
383
790
|
```
|
|
384
791
|
|
|
385
|
-
###
|
|
792
|
+
### Execute with Typed Response
|
|
793
|
+
|
|
794
|
+
```typescript
|
|
795
|
+
interface SalesData {
|
|
796
|
+
total_sales: number
|
|
797
|
+
orders_count: number
|
|
798
|
+
average_order_value: number
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
const analytics = new Analytics(taruviClient)
|
|
386
802
|
|
|
387
|
-
|
|
803
|
+
const result = await analytics.execute<SalesData>("sales-summary", {
|
|
804
|
+
params: { period: "monthly" }
|
|
805
|
+
})
|
|
806
|
+
|
|
807
|
+
console.log(result.data?.total_sales)
|
|
808
|
+
console.log(result.data?.orders_count)
|
|
809
|
+
```
|
|
810
|
+
|
|
811
|
+
### Complete Analytics Dashboard Example
|
|
388
812
|
|
|
389
813
|
```typescript
|
|
390
|
-
import {
|
|
391
|
-
import {
|
|
814
|
+
import { useEffect, useState } from 'react'
|
|
815
|
+
import { Analytics } from '@taruvi/sdk'
|
|
392
816
|
|
|
393
|
-
|
|
394
|
-
const
|
|
817
|
+
export default function AnalyticsDashboard({ taruviClient }) {
|
|
818
|
+
const [metrics, setMetrics] = useState(null)
|
|
819
|
+
const [loading, setLoading] = useState(true)
|
|
395
820
|
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
821
|
+
useEffect(() => {
|
|
822
|
+
const fetchMetrics = async () => {
|
|
823
|
+
try {
|
|
824
|
+
const analytics = new Analytics(taruviClient)
|
|
825
|
+
const result = await analytics.execute("dashboard-metrics", {
|
|
826
|
+
params: {
|
|
827
|
+
date_range: "last_30_days"
|
|
828
|
+
}
|
|
829
|
+
})
|
|
830
|
+
setMetrics(result.data)
|
|
831
|
+
} catch (error) {
|
|
832
|
+
console.error("Failed to fetch analytics:", error)
|
|
833
|
+
} finally {
|
|
834
|
+
setLoading(false)
|
|
835
|
+
}
|
|
836
|
+
}
|
|
837
|
+
fetchMetrics()
|
|
838
|
+
}, [])
|
|
403
839
|
|
|
404
|
-
|
|
405
|
-
}
|
|
840
|
+
if (loading) return <div>Loading analytics...</div>
|
|
406
841
|
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
842
|
+
return (
|
|
843
|
+
<div>
|
|
844
|
+
<h2>Dashboard Metrics</h2>
|
|
845
|
+
<pre>{JSON.stringify(metrics, null, 2)}</pre>
|
|
846
|
+
</div>
|
|
847
|
+
)
|
|
412
848
|
}
|
|
849
|
+
```
|
|
413
850
|
|
|
414
|
-
|
|
415
|
-
const client = useTaruvi()
|
|
416
|
-
return new Auth(client)
|
|
417
|
-
}
|
|
851
|
+
---
|
|
418
852
|
|
|
419
|
-
|
|
420
|
-
const client = useTaruvi()
|
|
421
|
-
return new User(client)
|
|
422
|
-
}
|
|
853
|
+
## Settings Service
|
|
423
854
|
|
|
424
|
-
|
|
425
|
-
import { ref, onMounted } from 'vue'
|
|
426
|
-
import { useUser } from '@/composables/taruvi'
|
|
855
|
+
### Get Site Settings
|
|
427
856
|
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
const user = useUser()
|
|
431
|
-
const userData = ref(null)
|
|
857
|
+
```typescript
|
|
858
|
+
import { Settings } from '@taruvi/sdk'
|
|
432
859
|
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
})
|
|
860
|
+
const settings = new Settings(taruviClient)
|
|
861
|
+
const config = await settings.get()
|
|
436
862
|
|
|
437
|
-
|
|
438
|
-
}
|
|
439
|
-
}
|
|
863
|
+
console.log(config) // Site configuration object
|
|
440
864
|
```
|
|
441
865
|
|
|
442
|
-
|
|
866
|
+
---
|
|
867
|
+
|
|
868
|
+
## Secrets Service
|
|
443
869
|
|
|
444
|
-
|
|
870
|
+
### List Secrets
|
|
445
871
|
|
|
446
872
|
```typescript
|
|
447
|
-
import {
|
|
873
|
+
import { Secrets } from '@taruvi/sdk'
|
|
448
874
|
|
|
449
|
-
|
|
450
|
-
const
|
|
451
|
-
apiKey: 'your-key',
|
|
452
|
-
appSlug: 'your-app',
|
|
453
|
-
baseUrl: 'https://test-api.taruvi.cloud'
|
|
454
|
-
})
|
|
875
|
+
const secrets = new Secrets(taruviClient)
|
|
876
|
+
const result = await secrets.list().execute()
|
|
455
877
|
|
|
456
|
-
//
|
|
457
|
-
|
|
458
|
-
const user = new User(client)
|
|
878
|
+
console.log(result.items) // Array of secrets
|
|
879
|
+
```
|
|
459
880
|
|
|
460
|
-
|
|
461
|
-
async function handleLogin() {
|
|
462
|
-
await auth.authenticateUser()
|
|
881
|
+
### Get Secret
|
|
463
882
|
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
883
|
+
```typescript
|
|
884
|
+
const secrets = new Secrets(taruviClient)
|
|
885
|
+
const secret = await secrets.get("MY_SECRET_KEY").execute()
|
|
886
|
+
|
|
887
|
+
console.log(secret) // Secret object with value
|
|
888
|
+
```
|
|
469
889
|
|
|
470
|
-
|
|
890
|
+
### Update Secret
|
|
891
|
+
|
|
892
|
+
```typescript
|
|
893
|
+
const secrets = new Secrets(taruviClient)
|
|
894
|
+
|
|
895
|
+
await secrets.update("MY_SECRET", {
|
|
896
|
+
value: {
|
|
897
|
+
hostname: "db.example.com",
|
|
898
|
+
port_number: 3306,
|
|
899
|
+
username: "admin",
|
|
900
|
+
password: "secret123"
|
|
901
|
+
},
|
|
902
|
+
tags: ["mysql", "production"],
|
|
903
|
+
secret_type: "Mysql"
|
|
904
|
+
}).execute()
|
|
471
905
|
```
|
|
472
906
|
|
|
473
907
|
---
|
|
474
908
|
|
|
475
|
-
##
|
|
909
|
+
## Policy Service (Resource Permissions)
|
|
476
910
|
|
|
477
|
-
|
|
911
|
+
### Check Resource Permissions
|
|
478
912
|
|
|
479
913
|
```typescript
|
|
480
|
-
import
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
914
|
+
import { Policy } from '@taruvi/sdk'
|
|
915
|
+
|
|
916
|
+
const policy = new Policy(taruviClient)
|
|
917
|
+
|
|
918
|
+
// Check permissions for multiple resources
|
|
919
|
+
const result = await policy.checkResource([
|
|
920
|
+
{
|
|
921
|
+
entityType: "crm",
|
|
922
|
+
tableName: "accounts",
|
|
923
|
+
recordId: "record-123",
|
|
924
|
+
attributes: { owner_id: "user-456" },
|
|
925
|
+
actions: ["read", "update"]
|
|
926
|
+
},
|
|
927
|
+
{
|
|
928
|
+
entityType: "docs",
|
|
929
|
+
tableName: "documents",
|
|
930
|
+
recordId: "doc-789",
|
|
931
|
+
attributes: {},
|
|
932
|
+
actions: ["delete"]
|
|
933
|
+
}
|
|
934
|
+
])
|
|
935
|
+
```
|
|
486
936
|
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
937
|
+
### Complete Permission Check Example
|
|
938
|
+
|
|
939
|
+
```typescript
|
|
940
|
+
import { useState, useEffect } from 'react'
|
|
941
|
+
import { Policy } from '@taruvi/sdk'
|
|
942
|
+
|
|
943
|
+
export default function ResourceGuard({ taruviClient, children, resource }) {
|
|
944
|
+
const [allowed, setAllowed] = useState(false)
|
|
945
|
+
const [loading, setLoading] = useState(true)
|
|
493
946
|
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
947
|
+
useEffect(() => {
|
|
948
|
+
const checkPermission = async () => {
|
|
949
|
+
try {
|
|
950
|
+
const policy = new Policy(taruviClient)
|
|
951
|
+
const result = await policy.checkResource([
|
|
952
|
+
{
|
|
953
|
+
entityType: resource.entityType,
|
|
954
|
+
tableName: resource.table,
|
|
955
|
+
recordId: resource.id,
|
|
956
|
+
attributes: resource.attributes || {},
|
|
957
|
+
actions: ["read"]
|
|
958
|
+
}
|
|
959
|
+
])
|
|
960
|
+
setAllowed(result.allowed)
|
|
961
|
+
} catch (error) {
|
|
962
|
+
console.error("Permission check failed:", error)
|
|
963
|
+
setAllowed(false)
|
|
964
|
+
} finally {
|
|
965
|
+
setLoading(false)
|
|
966
|
+
}
|
|
967
|
+
}
|
|
968
|
+
checkPermission()
|
|
969
|
+
}, [resource])
|
|
970
|
+
|
|
971
|
+
if (loading) return <div>Checking permissions...</div>
|
|
972
|
+
if (!allowed) return <div>Access denied</div>
|
|
973
|
+
return children
|
|
498
974
|
}
|
|
499
975
|
```
|
|
500
976
|
|
|
501
977
|
---
|
|
502
978
|
|
|
503
|
-
##
|
|
979
|
+
## App Service
|
|
504
980
|
|
|
505
|
-
###
|
|
981
|
+
### Get App Roles
|
|
506
982
|
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
1. **Dependency Injection** - No singletons, explicit dependencies
|
|
510
|
-
2. **Lazy Initialization** - Create only what you need
|
|
511
|
-
3. **Internal API Protection** - Internal utilities marked with `@internal`
|
|
512
|
-
4. **Type Safety** - Full TypeScript support with strict mode
|
|
513
|
-
5. **Tree-Shaking** - Unused code is eliminated by bundlers
|
|
983
|
+
```typescript
|
|
984
|
+
import { App } from '@taruvi/sdk'
|
|
514
985
|
|
|
515
|
-
|
|
986
|
+
const app = new App(taruviClient)
|
|
987
|
+
const roles = await app.roles().execute()
|
|
516
988
|
|
|
989
|
+
console.log(roles) // Array of role objects with id, name, permissions
|
|
517
990
|
```
|
|
518
|
-
src/
|
|
519
|
-
├── lib/ # Public API (safe to use)
|
|
520
|
-
│ ├── auth/ # Auth service
|
|
521
|
-
│ ├── user/ # User service
|
|
522
|
-
│ ├── storage/ # Storage service
|
|
523
|
-
│ ├── settings/ # Settings service
|
|
524
|
-
│ ├── database/ # Database service (planned)
|
|
525
|
-
│ └── function/ # Functions service (planned)
|
|
526
|
-
│
|
|
527
|
-
├── lib-internal/ # Internal utilities (not public)
|
|
528
|
-
│ ├── http/ # HTTP client wrapper
|
|
529
|
-
│ ├── token/ # Token management
|
|
530
|
-
│ ├── routes/ # API route definitions
|
|
531
|
-
│ ├── errors/ # Error handling
|
|
532
|
-
│ └── utils/ # Helper functions
|
|
533
|
-
│
|
|
534
|
-
├── client.ts # Main Client class
|
|
535
|
-
├── index.ts # Public exports
|
|
536
|
-
└── types.ts # Shared types
|
|
537
|
-
```
|
|
538
991
|
|
|
539
|
-
###
|
|
992
|
+
### Complete Roles List Example
|
|
993
|
+
|
|
994
|
+
```typescript
|
|
995
|
+
import { useEffect, useState } from 'react'
|
|
996
|
+
import { App } from '@taruvi/sdk'
|
|
540
997
|
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
998
|
+
export default function RolesList({ taruviClient }) {
|
|
999
|
+
const [roles, setRoles] = useState([])
|
|
1000
|
+
const [loading, setLoading] = useState(true)
|
|
1001
|
+
|
|
1002
|
+
useEffect(() => {
|
|
1003
|
+
const fetchRoles = async () => {
|
|
1004
|
+
try {
|
|
1005
|
+
const app = new App(taruviClient)
|
|
1006
|
+
const response = await app.roles().execute()
|
|
1007
|
+
setRoles(response.data || [])
|
|
1008
|
+
} catch (error) {
|
|
1009
|
+
console.error("Failed to fetch roles:", error)
|
|
1010
|
+
} finally {
|
|
1011
|
+
setLoading(false)
|
|
1012
|
+
}
|
|
1013
|
+
}
|
|
1014
|
+
fetchRoles()
|
|
1015
|
+
}, [])
|
|
546
1016
|
|
|
547
|
-
|
|
548
|
-
- `client.httpClient` - Marked with `@internal`
|
|
549
|
-
- `client.tokenClient` - Marked with `@internal`
|
|
550
|
-
- Files in `lib-internal/` folder
|
|
1017
|
+
if (loading) return <div>Loading roles...</div>
|
|
551
1018
|
|
|
552
|
-
|
|
1019
|
+
return (
|
|
1020
|
+
<ul>
|
|
1021
|
+
{roles.map(role => (
|
|
1022
|
+
<li key={role.id}>
|
|
1023
|
+
<strong>{role.name}</strong>
|
|
1024
|
+
<span>{role.permissions?.join(", ")}</span>
|
|
1025
|
+
</li>
|
|
1026
|
+
))}
|
|
1027
|
+
</ul>
|
|
1028
|
+
)
|
|
1029
|
+
}
|
|
1030
|
+
```
|
|
553
1031
|
|
|
554
1032
|
---
|
|
555
1033
|
|
|
556
|
-
##
|
|
1034
|
+
## Common Patterns
|
|
557
1035
|
|
|
558
|
-
###
|
|
1036
|
+
### Loading States
|
|
559
1037
|
|
|
560
1038
|
```typescript
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
1039
|
+
const [data, setData] = useState(null)
|
|
1040
|
+
const [loading, setLoading] = useState(true)
|
|
1041
|
+
const [error, setError] = useState(null)
|
|
1042
|
+
|
|
1043
|
+
useEffect(() => {
|
|
1044
|
+
const fetchData = async () => {
|
|
1045
|
+
setLoading(true)
|
|
1046
|
+
try {
|
|
1047
|
+
const db = new Database(taruviClient)
|
|
1048
|
+
const response = await db.from("table").execute()
|
|
1049
|
+
setData(response.data)
|
|
1050
|
+
} catch (err) {
|
|
1051
|
+
setError(err.message)
|
|
1052
|
+
} finally {
|
|
1053
|
+
setLoading(false)
|
|
572
1054
|
}
|
|
1055
|
+
}
|
|
1056
|
+
fetchData()
|
|
1057
|
+
}, [])
|
|
573
1058
|
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
apiKey: 'test',
|
|
579
|
-
appSlug: 'test',
|
|
580
|
-
baseUrl: 'test'
|
|
581
|
-
})
|
|
582
|
-
} as unknown as Client
|
|
1059
|
+
if (loading) return <div>Loading...</div>
|
|
1060
|
+
if (error) return <div>Error: {error}</div>
|
|
1061
|
+
return <div>{/* Render data */}</div>
|
|
1062
|
+
```
|
|
583
1063
|
|
|
584
|
-
|
|
585
|
-
const user = new User(mockClient)
|
|
586
|
-
const data = await user.getUserData()
|
|
1064
|
+
### Error Handling
|
|
587
1065
|
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
1066
|
+
```typescript
|
|
1067
|
+
try {
|
|
1068
|
+
const user = new User(taruviClient)
|
|
1069
|
+
await user.createUser(data)
|
|
1070
|
+
} catch (error) {
|
|
1071
|
+
if (error.response?.status === 400) {
|
|
1072
|
+
console.error("Validation error:", error.response.data)
|
|
1073
|
+
} else if (error.response?.status === 401) {
|
|
1074
|
+
console.error("Unauthorized")
|
|
1075
|
+
} else {
|
|
1076
|
+
console.error("Unknown error:", error.message)
|
|
1077
|
+
}
|
|
1078
|
+
}
|
|
592
1079
|
```
|
|
593
1080
|
|
|
594
|
-
|
|
1081
|
+
### Refresh Data After Mutation
|
|
595
1082
|
|
|
596
|
-
|
|
1083
|
+
```typescript
|
|
1084
|
+
const [items, setItems] = useState([])
|
|
1085
|
+
|
|
1086
|
+
const fetchItems = async () => {
|
|
1087
|
+
const db = new Database(taruviClient)
|
|
1088
|
+
const response = await db.from("items").execute()
|
|
1089
|
+
setItems(response.data || [])
|
|
1090
|
+
}
|
|
597
1091
|
|
|
598
|
-
|
|
1092
|
+
const createItem = async (data) => {
|
|
1093
|
+
const db = new Database(taruviClient)
|
|
1094
|
+
await db.from("items").create(data).execute()
|
|
1095
|
+
await fetchItems() // Refresh list
|
|
1096
|
+
}
|
|
599
1097
|
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
| Storage Service | 🚧 Partial | 70% | Query builder working |
|
|
606
|
-
| Settings Service | ✅ Functional | 70% | Site config fetching |
|
|
607
|
-
| Database Service | 📋 Planned | 0% | Not yet implemented |
|
|
608
|
-
| Functions Service | 📋 Planned | 0% | Not yet implemented |
|
|
1098
|
+
const deleteItem = async (id) => {
|
|
1099
|
+
const db = new Database(taruviClient)
|
|
1100
|
+
await db.from("items").delete(id).execute()
|
|
1101
|
+
await fetchItems() // Refresh list
|
|
1102
|
+
}
|
|
609
1103
|
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
1104
|
+
const updateItem = async (id, data) => {
|
|
1105
|
+
const db = new Database(taruviClient)
|
|
1106
|
+
await db.from("items").get(id).update(data).execute()
|
|
1107
|
+
await fetchItems() // Refresh list
|
|
1108
|
+
}
|
|
1109
|
+
```
|
|
614
1110
|
|
|
615
1111
|
---
|
|
616
1112
|
|
|
617
|
-
##
|
|
1113
|
+
## TypeScript Types
|
|
1114
|
+
|
|
1115
|
+
### Import Types
|
|
1116
|
+
|
|
1117
|
+
```typescript
|
|
1118
|
+
import type {
|
|
1119
|
+
TaruviConfig,
|
|
1120
|
+
AuthTokens,
|
|
1121
|
+
UserCreateRequest,
|
|
1122
|
+
UserResponse,
|
|
1123
|
+
UserDataResponse,
|
|
1124
|
+
FunctionRequest,
|
|
1125
|
+
FunctionResponse,
|
|
1126
|
+
FunctionInvocation,
|
|
1127
|
+
DatabaseRequest,
|
|
1128
|
+
DatabaseResponse,
|
|
1129
|
+
DatabaseFilters,
|
|
1130
|
+
StorageRequest,
|
|
1131
|
+
StorageUpdateRequest,
|
|
1132
|
+
StorageResponse,
|
|
1133
|
+
StorageFilters,
|
|
1134
|
+
SettingsResponse,
|
|
1135
|
+
SecretRequest,
|
|
1136
|
+
SecretResponse,
|
|
1137
|
+
Principal,
|
|
1138
|
+
Resource,
|
|
1139
|
+
Resources,
|
|
1140
|
+
RoleResponse,
|
|
1141
|
+
AnalyticsRequest,
|
|
1142
|
+
AnalyticsResponse
|
|
1143
|
+
} from '@taruvi/sdk'
|
|
1144
|
+
```
|
|
618
1145
|
|
|
619
|
-
###
|
|
620
|
-
- [ ] Complete Auth service (SSO, token refresh, sign out)
|
|
621
|
-
- [ ] Add error handling classes
|
|
622
|
-
- [ ] Implement token management (set, clear, expiration)
|
|
623
|
-
- [ ] Add PATCH HTTP method
|
|
624
|
-
- [ ] Add retry logic for failed requests
|
|
1146
|
+
### Type Usage
|
|
625
1147
|
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
1148
|
+
```typescript
|
|
1149
|
+
const config: TaruviConfig = {
|
|
1150
|
+
apiKey: "key",
|
|
1151
|
+
appSlug: "app",
|
|
1152
|
+
baseUrl: "https://api.taruvi.cloud",
|
|
1153
|
+
deskUrl: "https://desk.taruvi.cloud", // optional
|
|
1154
|
+
token: "existing-token" // optional
|
|
1155
|
+
}
|
|
631
1156
|
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
1157
|
+
const userData: UserCreateRequest = {
|
|
1158
|
+
username: "john",
|
|
1159
|
+
email: "john@example.com",
|
|
1160
|
+
password: "pass123",
|
|
1161
|
+
confirm_password: "pass123",
|
|
1162
|
+
first_name: "John",
|
|
1163
|
+
last_name: "Doe",
|
|
1164
|
+
is_active: true,
|
|
1165
|
+
is_staff: false
|
|
1166
|
+
}
|
|
636
1167
|
|
|
637
|
-
|
|
1168
|
+
// Database filters with operators
|
|
1169
|
+
const dbFilters: DatabaseFilters = {
|
|
1170
|
+
page: 1,
|
|
1171
|
+
pageSize: 20,
|
|
1172
|
+
ordering: "-created_at",
|
|
1173
|
+
status: "active",
|
|
1174
|
+
age__gte: 18,
|
|
1175
|
+
name__icontains: "john"
|
|
1176
|
+
}
|
|
638
1177
|
|
|
639
|
-
|
|
1178
|
+
// Storage filters with comprehensive options
|
|
1179
|
+
const storageFilters: StorageFilters = {
|
|
1180
|
+
page: 1,
|
|
1181
|
+
pageSize: 50,
|
|
1182
|
+
search: "invoice",
|
|
1183
|
+
visibility: "public",
|
|
1184
|
+
mimetype_category: "document",
|
|
1185
|
+
size__gte: 1024,
|
|
1186
|
+
size__lte: 10485760,
|
|
1187
|
+
created_at__gte: "2024-01-01",
|
|
1188
|
+
ordering: "-created_at",
|
|
1189
|
+
created_by_me: true
|
|
1190
|
+
}
|
|
640
1191
|
|
|
641
|
-
|
|
1192
|
+
// Policy types for permission checking
|
|
1193
|
+
const principal: Principal = {
|
|
1194
|
+
id: "user-123",
|
|
1195
|
+
roles: ["admin", "editor"],
|
|
1196
|
+
attr: { department: "engineering" }
|
|
1197
|
+
}
|
|
642
1198
|
|
|
643
|
-
|
|
1199
|
+
const resources: Resources = [
|
|
1200
|
+
{
|
|
1201
|
+
entityType: "crm",
|
|
1202
|
+
tableName: "accounts",
|
|
1203
|
+
recordId: "acc-456",
|
|
1204
|
+
attributes: { owner_id: "user-123" },
|
|
1205
|
+
actions: ["read", "update", "delete"]
|
|
1206
|
+
}
|
|
1207
|
+
]
|
|
1208
|
+
```
|
|
644
1209
|
|
|
645
1210
|
---
|
|
646
1211
|
|
|
647
|
-
##
|
|
1212
|
+
## Filter Operators Reference
|
|
648
1213
|
|
|
649
|
-
|
|
1214
|
+
### Database Filter Operators (Django-style)
|
|
650
1215
|
|
|
651
|
-
|
|
1216
|
+
The Database service supports Django-style field lookups:
|
|
652
1217
|
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
1218
|
+
| Operator | Description | Example |
|
|
1219
|
+
|----------|-------------|---------|
|
|
1220
|
+
| `field` | Exact match | `{ status: "active" }` |
|
|
1221
|
+
| `field__gte` | Greater than or equal | `{ age__gte: 18 }` |
|
|
1222
|
+
| `field__gt` | Greater than | `{ age__gt: 17 }` |
|
|
1223
|
+
| `field__lte` | Less than or equal | `{ age__lte: 65 }` |
|
|
1224
|
+
| `field__lt` | Less than | `{ age__lt: 66 }` |
|
|
1225
|
+
| `field__icontains` | Case-insensitive contains | `{ name__icontains: "john" }` |
|
|
1226
|
+
| `field__contains` | Case-sensitive contains | `{ name__contains: "John" }` |
|
|
1227
|
+
| `field__istartswith` | Case-insensitive starts with | `{ email__istartswith: "admin" }` |
|
|
1228
|
+
| `field__startswith` | Case-sensitive starts with | `{ code__startswith: "PRE" }` |
|
|
1229
|
+
| `field__iendswith` | Case-insensitive ends with | `{ domain__iendswith: ".com" }` |
|
|
1230
|
+
| `field__endswith` | Case-sensitive ends with | `{ filename__endswith: ".pdf" }` |
|
|
1231
|
+
| `field__in` | Value in list | `{ status__in: ["active", "pending"] }` |
|
|
1232
|
+
| `field__isnull` | Is null check | `{ deleted_at__isnull: true }` |
|
|
1233
|
+
| `ordering` | Sort results | `{ ordering: "-created_at" }` (- for desc) |
|
|
1234
|
+
| `page` | Page number | `{ page: 1 }` |
|
|
1235
|
+
| `pageSize` | Items per page | `{ pageSize: 20 }` |
|
|
657
1236
|
|
|
658
|
-
|
|
659
|
-
npm install
|
|
1237
|
+
### Populate (Eager Loading)
|
|
660
1238
|
|
|
661
|
-
|
|
662
|
-
npm run build
|
|
1239
|
+
Use the `populate()` method to eager load related records:
|
|
663
1240
|
|
|
664
|
-
|
|
665
|
-
|
|
1241
|
+
```typescript
|
|
1242
|
+
// Populate accepts an array of relation field names
|
|
1243
|
+
db.from("orders").populate(["customer", "items"]).execute()
|
|
1244
|
+
|
|
1245
|
+
// This adds ?populate=customer,items to the query string
|
|
666
1246
|
```
|
|
667
1247
|
|
|
668
|
-
|
|
1248
|
+
| Parameter | Type | Description |
|
|
1249
|
+
|-----------|------|-------------|
|
|
1250
|
+
| `populate` | `string[]` | Array of relation field names to eager load |
|
|
669
1251
|
|
|
670
|
-
|
|
1252
|
+
### Storage Filter Options
|
|
671
1253
|
|
|
672
|
-
|
|
1254
|
+
The Storage service supports these specialized filters:
|
|
673
1255
|
|
|
674
|
-
|
|
1256
|
+
| Category | Filters | Description |
|
|
1257
|
+
|----------|---------|-------------|
|
|
1258
|
+
| **Size** | `size__gte`, `size__lte`, `size__gt`, `size__lt`, `min_size`, `max_size` | Filter by file size in bytes |
|
|
1259
|
+
| **Dates** | `created_at__gte`, `created_at__lte`, `created_after`, `created_before`, `updated_at__gte`, `updated_at__lte` | Filter by dates (ISO 8601 format) |
|
|
1260
|
+
| **Search** | `search`, `filename__icontains`, `prefix`, `file`, `file__icontains`, `file__startswith`, `file__istartswith`, `metadata_search` | Search for files |
|
|
1261
|
+
| **MIME Type** | `mimetype`, `mimetype__in`, `mimetype_category` | Filter by file type (document, image, video, audio, etc.) |
|
|
1262
|
+
| **Visibility** | `visibility` | Filter by public/private visibility |
|
|
1263
|
+
| **User** | `created_by_me`, `modified_by_me`, `created_by__username`, `created_by__username__icontains` | Filter by user |
|
|
1264
|
+
| **Pagination** | `page`, `pageSize` | Paginate results |
|
|
1265
|
+
| **Sorting** | `ordering` | Sort results (e.g., "-created_at") |
|
|
675
1266
|
|
|
676
|
-
|
|
1267
|
+
---
|
|
677
1268
|
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
1269
|
+
## Quick Reference
|
|
1270
|
+
|
|
1271
|
+
| Service | Import | Purpose |
|
|
1272
|
+
|---------|--------|---------|
|
|
1273
|
+
| `Client` | `import { Client }` | Main SDK client |
|
|
1274
|
+
| `Auth` | `import { Auth }` | Authentication |
|
|
1275
|
+
| `User` | `import { User }` | User management |
|
|
1276
|
+
| `Database` | `import { Database }` | App data CRUD |
|
|
1277
|
+
| `Storage` | `import { Storage }` | File management |
|
|
1278
|
+
| `Functions` | `import { Functions }` | Serverless functions |
|
|
1279
|
+
| `Settings` | `import { Settings }` | Site configuration |
|
|
1280
|
+
| `Secrets` | `import { Secrets }` | Sensitive data |
|
|
1281
|
+
| `Policy` | `import { Policy }` | Resource permissions |
|
|
1282
|
+
| `App` | `import { App }` | App roles & config |
|
|
1283
|
+
| `Analytics` | `import { Analytics }` | Analytics queries |
|
|
682
1284
|
|
|
683
1285
|
---
|
|
684
1286
|
|
|
685
|
-
##
|
|
1287
|
+
## Chaining Pattern
|
|
686
1288
|
|
|
687
|
-
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
1289
|
+
All query-building services use method chaining:
|
|
1290
|
+
|
|
1291
|
+
```typescript
|
|
1292
|
+
// Database
|
|
1293
|
+
const db = new Database(taruviClient)
|
|
1294
|
+
await db.from("table").get("id").update(data).execute()
|
|
1295
|
+
await db.from("table").filter({ status: "active" }).execute()
|
|
1296
|
+
await db.from("table").filter({ page: 1 }).populate(["related_field"]).execute()
|
|
1297
|
+
await db.from("table").create({ name: "New" }).execute()
|
|
1298
|
+
|
|
1299
|
+
// Storage
|
|
1300
|
+
const storage = new Storage(taruviClient)
|
|
1301
|
+
await storage.from("bucket").delete(["path/to/file.pdf"]).execute()
|
|
1302
|
+
await storage.from("bucket").filter({ search: "query" }).execute()
|
|
1303
|
+
await storage.from("bucket").download("path/to/file.pdf").execute()
|
|
1304
|
+
```
|
|
1305
|
+
|
|
1306
|
+
**Always call `.execute()` at the end to run the query!**
|
|
691
1307
|
|
|
692
1308
|
---
|
|
693
1309
|
|
|
694
|
-
##
|
|
1310
|
+
## Environment Variables
|
|
1311
|
+
|
|
1312
|
+
```env
|
|
1313
|
+
VITE_TARUVI_API_KEY=your-api-key
|
|
1314
|
+
VITE_TARUVI_APP_SLUG=your-app
|
|
1315
|
+
VITE_TARUVI_BASE_URL=https://taruvi-site.taruvi.cloud
|
|
1316
|
+
```
|
|
695
1317
|
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
1318
|
+
```typescript
|
|
1319
|
+
const client = new Client({
|
|
1320
|
+
apiKey: import.meta.env.VITE_TARUVI_API_KEY,
|
|
1321
|
+
appSlug: import.meta.env.VITE_TARUVI_APP_SLUG,
|
|
1322
|
+
baseUrl: import.meta.env.VITE_TARUVI_BASE_URL
|
|
1323
|
+
})
|
|
1324
|
+
```
|
|
700
1325
|
|
|
701
1326
|
---
|
|
702
1327
|
|
|
703
|
-
**
|
|
1328
|
+
**Generated from production code examples • Last updated: 2026-01-05**
|