@blinkdotnew/sdk 0.14.9 → 0.14.11
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 +89 -15
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -35,8 +35,8 @@ const user = await blink.auth.me()
|
|
|
35
35
|
|
|
36
36
|
// Database operations (zero config)
|
|
37
37
|
const todos = await blink.db.todos.list({
|
|
38
|
-
where: {
|
|
39
|
-
orderBy: {
|
|
38
|
+
where: { userId: user.id },
|
|
39
|
+
orderBy: { createdAt: 'desc' },
|
|
40
40
|
limit: 20
|
|
41
41
|
})
|
|
42
42
|
|
|
@@ -187,9 +187,13 @@ await blink.auth.updateMe({ displayName: 'New Name' })
|
|
|
187
187
|
blink.auth.setToken(jwt, persist?)
|
|
188
188
|
const isAuth = blink.auth.isAuthenticated()
|
|
189
189
|
|
|
190
|
-
// Auth state listener
|
|
190
|
+
// Auth state listener (REQUIRED for React apps!)
|
|
191
191
|
const unsubscribe = blink.auth.onAuthStateChanged((state) => {
|
|
192
192
|
console.log('Auth state:', state)
|
|
193
|
+
// state.user - current user or null
|
|
194
|
+
// state.isLoading - true while auth is initializing
|
|
195
|
+
// state.isAuthenticated - true if user is logged in
|
|
196
|
+
// state.tokens - current auth tokens
|
|
193
197
|
})
|
|
194
198
|
```
|
|
195
199
|
|
|
@@ -201,6 +205,11 @@ The SDK now automatically converts between JavaScript camelCase and SQL snake_ca
|
|
|
201
205
|
- **Stored as snake_case**: `user_id`, `created_at`, `is_completed`
|
|
202
206
|
- **No manual conversion needed!**
|
|
203
207
|
|
|
208
|
+
**⚠️ Important: Always Use camelCase in Your Code**
|
|
209
|
+
- ✅ **Correct**: `{ userId: user.id, createdAt: new Date() }`
|
|
210
|
+
- ❌ **Wrong**: `{ user_id: user.id, created_at: new Date() }`
|
|
211
|
+
- **Exception**: Raw SQL queries still use snake_case (as stored in database)
|
|
212
|
+
|
|
204
213
|
```typescript
|
|
205
214
|
// Create (ID auto-generated if not provided)
|
|
206
215
|
const todo = await blink.db.todos.create({
|
|
@@ -226,23 +235,23 @@ const todos = await blink.db.todos.list({
|
|
|
226
235
|
|
|
227
236
|
// Note: Boolean fields are returned as "0"/"1" strings from SQLite
|
|
228
237
|
// Check boolean values using Number(value) > 0
|
|
229
|
-
const completedTodos = todos.filter(todo => Number(todo.
|
|
230
|
-
const incompleteTodos = todos.filter(todo => Number(todo.
|
|
238
|
+
const completedTodos = todos.filter(todo => Number(todo.isCompleted) > 0)
|
|
239
|
+
const incompleteTodos = todos.filter(todo => Number(todo.isCompleted) === 0)
|
|
231
240
|
|
|
232
241
|
// Update
|
|
233
|
-
await blink.db.todos.update(todo.id, {
|
|
242
|
+
await blink.db.todos.update(todo.id, { isCompleted: true })
|
|
234
243
|
|
|
235
244
|
// Delete
|
|
236
245
|
await blink.db.todos.delete(todo.id)
|
|
237
246
|
|
|
238
247
|
// Bulk operations (IDs auto-generated if not provided)
|
|
239
248
|
await blink.db.todos.createMany([
|
|
240
|
-
{ title: 'Task 1',
|
|
241
|
-
{ id: 'custom_id', title: 'Task 2',
|
|
249
|
+
{ title: 'Task 1', userId: user.id }, // ID will be auto-generated
|
|
250
|
+
{ id: 'custom_id', title: 'Task 2', userId: user.id } // Custom ID provided
|
|
242
251
|
])
|
|
243
252
|
await blink.db.todos.upsertMany([...])
|
|
244
253
|
|
|
245
|
-
// Raw SQL
|
|
254
|
+
// Raw SQL (note: raw SQL still uses snake_case as stored in database)
|
|
246
255
|
const result = await blink.db.sql('SELECT * FROM todos WHERE user_id = ?', [user.id])
|
|
247
256
|
```
|
|
248
257
|
|
|
@@ -1205,9 +1214,9 @@ The SDK is written in TypeScript and provides full type safety:
|
|
|
1205
1214
|
interface Todo {
|
|
1206
1215
|
id: string
|
|
1207
1216
|
title: string
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1217
|
+
isCompleted: boolean // Will be returned as "0" or "1" string from SQLite
|
|
1218
|
+
userId: string // Automatically converted from snake_case user_id
|
|
1219
|
+
createdAt: string // Automatically converted from snake_case created_at
|
|
1211
1220
|
}
|
|
1212
1221
|
|
|
1213
1222
|
// Note: Boolean fields are returned as "0"/"1" strings from SQLite
|
|
@@ -1215,12 +1224,12 @@ interface Todo {
|
|
|
1215
1224
|
const todos = await blink.db.todos.list<Todo>()
|
|
1216
1225
|
|
|
1217
1226
|
// Check boolean values properly
|
|
1218
|
-
const completedTodos = todos.filter(todo => Number(todo.
|
|
1219
|
-
const incompleteTodos = todos.filter(todo => Number(todo.
|
|
1227
|
+
const completedTodos = todos.filter(todo => Number(todo.isCompleted) > 0)
|
|
1228
|
+
const incompleteTodos = todos.filter(todo => Number(todo.isCompleted) === 0)
|
|
1220
1229
|
|
|
1221
1230
|
// When filtering by boolean values in queries, use "0"/"1" strings
|
|
1222
1231
|
const onlyCompleted = await blink.db.todos.list<Todo>({
|
|
1223
|
-
where: {
|
|
1232
|
+
where: { isCompleted: "1" } // Use string "1" for true, "0" for false
|
|
1224
1233
|
})
|
|
1225
1234
|
// todos is fully typed as Todo[]
|
|
1226
1235
|
```
|
|
@@ -1390,6 +1399,44 @@ function MyRealtimeComponent() {
|
|
|
1390
1399
|
|
|
1391
1400
|
### React
|
|
1392
1401
|
|
|
1402
|
+
**⚠️ Critical: Always Use Auth State Listener, Never One-Time Checks**
|
|
1403
|
+
|
|
1404
|
+
The most common authentication mistake is checking auth status once instead of listening to changes:
|
|
1405
|
+
|
|
1406
|
+
```typescript
|
|
1407
|
+
// ❌ WRONG - One-time check misses auth completion
|
|
1408
|
+
useEffect(() => {
|
|
1409
|
+
const checkAuth = async () => {
|
|
1410
|
+
try {
|
|
1411
|
+
const userData = await blink.auth.me()
|
|
1412
|
+
setUser(userData)
|
|
1413
|
+
} catch (error) {
|
|
1414
|
+
console.error('Auth check failed:', error)
|
|
1415
|
+
} finally {
|
|
1416
|
+
setLoading(false)
|
|
1417
|
+
}
|
|
1418
|
+
}
|
|
1419
|
+
checkAuth() // Only runs once - misses when auth completes later!
|
|
1420
|
+
}, [])
|
|
1421
|
+
|
|
1422
|
+
// ✅ CORRECT - Listen to auth state changes
|
|
1423
|
+
useEffect(() => {
|
|
1424
|
+
const unsubscribe = blink.auth.onAuthStateChanged((state) => {
|
|
1425
|
+
setUser(state.user)
|
|
1426
|
+
setLoading(state.isLoading)
|
|
1427
|
+
})
|
|
1428
|
+
return unsubscribe
|
|
1429
|
+
}, [])
|
|
1430
|
+
```
|
|
1431
|
+
|
|
1432
|
+
**Why the one-time check fails:**
|
|
1433
|
+
1. App loads → `blink.auth.me()` called immediately
|
|
1434
|
+
2. Auth still initializing → Call fails, user set to `null`
|
|
1435
|
+
3. Auth completes later → App never knows because it only checked once
|
|
1436
|
+
4. User stuck on "Please sign in" screen forever
|
|
1437
|
+
|
|
1438
|
+
**Always use `onAuthStateChanged()` for React apps!**
|
|
1439
|
+
|
|
1393
1440
|
```typescript
|
|
1394
1441
|
import { createClient } from '@blinkdotnew/sdk'
|
|
1395
1442
|
import { useState, useEffect } from 'react'
|
|
@@ -1398,19 +1445,46 @@ const blink = createClient({ projectId: 'your-project', authRequired: true })
|
|
|
1398
1445
|
|
|
1399
1446
|
function App() {
|
|
1400
1447
|
const [user, setUser] = useState(null)
|
|
1448
|
+
const [loading, setLoading] = useState(true)
|
|
1401
1449
|
|
|
1402
1450
|
useEffect(() => {
|
|
1403
1451
|
const unsubscribe = blink.auth.onAuthStateChanged((state) => {
|
|
1404
1452
|
setUser(state.user)
|
|
1453
|
+
setLoading(state.isLoading)
|
|
1405
1454
|
})
|
|
1406
1455
|
return unsubscribe
|
|
1407
1456
|
}, [])
|
|
1408
1457
|
|
|
1458
|
+
if (loading) return <div>Loading...</div>
|
|
1409
1459
|
if (!user) return <div>Please log in</div>
|
|
1410
1460
|
|
|
1411
1461
|
return <div>Welcome, {user.email}!</div>
|
|
1412
1462
|
}
|
|
1413
1463
|
|
|
1464
|
+
// ❌ WRONG - One-time auth check (misses auth state changes)
|
|
1465
|
+
function BadApp() {
|
|
1466
|
+
const [user, setUser] = useState(null)
|
|
1467
|
+
const [loading, setLoading] = useState(true)
|
|
1468
|
+
|
|
1469
|
+
useEffect(() => {
|
|
1470
|
+
const checkAuth = async () => {
|
|
1471
|
+
try {
|
|
1472
|
+
const userData = await blink.auth.me()
|
|
1473
|
+
setUser(userData)
|
|
1474
|
+
} catch (error) {
|
|
1475
|
+
console.error('Auth check failed:', error)
|
|
1476
|
+
} finally {
|
|
1477
|
+
setLoading(false)
|
|
1478
|
+
}
|
|
1479
|
+
}
|
|
1480
|
+
|
|
1481
|
+
checkAuth() // ❌ Only runs once - misses when auth completes later!
|
|
1482
|
+
}, [])
|
|
1483
|
+
|
|
1484
|
+
// This pattern causes users to get stuck on "Please sign in"
|
|
1485
|
+
// even after authentication completes
|
|
1486
|
+
}
|
|
1487
|
+
|
|
1414
1488
|
// React example with search functionality
|
|
1415
1489
|
function SearchResults() {
|
|
1416
1490
|
const [query, setQuery] = useState('')
|
package/package.json
CHANGED