chub-dev 0.2.0-beta.3 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +76 -0
- package/bin/chub-mcp +2 -0
- package/package.json +11 -6
- package/skills/get-api-docs/SKILL.md +81 -0
- package/src/commands/annotate.js +83 -0
- package/src/commands/build.js +21 -4
- package/src/commands/feedback.js +12 -9
- package/src/commands/get.js +77 -12
- package/src/commands/help.js +34 -0
- package/src/commands/search.js +17 -8
- package/src/index.js +35 -67
- package/src/lib/analytics.js +13 -2
- package/src/lib/annotations.js +57 -0
- package/src/lib/bm25.js +303 -0
- package/src/lib/cache.js +108 -17
- package/src/lib/config.js +15 -2
- package/src/lib/help.js +158 -0
- package/src/lib/identity.js +12 -1
- package/src/lib/registry.js +283 -27
- package/src/lib/telemetry.js +7 -1
- package/src/lib/welcome.js +42 -0
- package/src/mcp/server.js +184 -0
- package/src/mcp/stdio-lifecycle.js +54 -0
- package/src/mcp/tools.js +286 -0
- package/dist/anthropic/docs/sdk/javascript/DOC.md +0 -499
- package/dist/anthropic/docs/sdk/python/DOC.md +0 -382
- package/dist/openai/docs/chat/javascript/DOC.md +0 -350
- package/dist/openai/docs/chat/python/DOC.md +0 -526
- package/dist/pinecone/docs/sdk/javascript/DOC.md +0 -984
- package/dist/pinecone/docs/sdk/python/DOC.md +0 -1395
- package/dist/registry.json +0 -276
- package/dist/resend/docs/sdk/DOC.md +0 -1271
- package/dist/stripe/docs/api/DOC.md +0 -1726
- package/dist/supabase/docs/sdk/DOC.md +0 -1606
- package/dist/twilio/docs/sdk/python/DOC.md +0 -469
- package/dist/twilio/docs/sdk/typescript/DOC.md +0 -946
|
@@ -1,1606 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: sdk
|
|
3
|
-
description: "Open-source Firebase alternative with PostgreSQL backend, authentication, storage, realtime subscriptions, and edge functions"
|
|
4
|
-
metadata:
|
|
5
|
-
languages: "javascript"
|
|
6
|
-
versions: "2.76.1"
|
|
7
|
-
updated-on: "2025-10-26"
|
|
8
|
-
source: maintainer
|
|
9
|
-
tags: "supabase,sdk,database,auth,storage,realtime"
|
|
10
|
-
---
|
|
11
|
-
# Supabase JavaScript SDK Coding Guidelines
|
|
12
|
-
|
|
13
|
-
You are a Supabase coding expert. Help me write code using the Supabase JavaScript SDK, which provides a complete backend-as-a-service solution with authentication, database, storage, realtime subscriptions, and edge functions.
|
|
14
|
-
|
|
15
|
-
You can find the official SDK documentation here:
|
|
16
|
-
https://supabase.com/docs/reference/javascript/introduction
|
|
17
|
-
|
|
18
|
-
## Golden Rule: Use the Correct and Current SDK
|
|
19
|
-
|
|
20
|
-
Always use the official Supabase JavaScript SDK (`@supabase/supabase-js`) for all Supabase interactions. Do not use deprecated libraries or unofficial packages.
|
|
21
|
-
|
|
22
|
-
- **Library Name:** Supabase JavaScript SDK
|
|
23
|
-
- **NPM Package:** `@supabase/supabase-js`
|
|
24
|
-
- **Current Version:** 2.76.1
|
|
25
|
-
- **Legacy Libraries:** Do not use `@supabase/auth-js`, `@supabase/postgrest-js`, `@supabase/storage-js`, or other individual packages directly - these are bundled in the main SDK
|
|
26
|
-
|
|
27
|
-
**Installation:**
|
|
28
|
-
|
|
29
|
-
```bash
|
|
30
|
-
npm install @supabase/supabase-js
|
|
31
|
-
```
|
|
32
|
-
|
|
33
|
-
**APIs and Usage:**
|
|
34
|
-
|
|
35
|
-
- **Correct:** `import { createClient } from '@supabase/supabase-js'`
|
|
36
|
-
- **Correct:** `const supabase = createClient(url, anonKey)`
|
|
37
|
-
- **Correct:** `await supabase.from('table').select()`
|
|
38
|
-
- **Correct:** `await supabase.auth.signInWithPassword()`
|
|
39
|
-
- **Incorrect:** Using individual package imports
|
|
40
|
-
- **Incorrect:** `new Supabase()` or `SupabaseClient()`
|
|
41
|
-
|
|
42
|
-
## Initialization
|
|
43
|
-
|
|
44
|
-
The Supabase SDK requires your project URL and public anon key for initialization.
|
|
45
|
-
|
|
46
|
-
### Basic Client Setup
|
|
47
|
-
|
|
48
|
-
```javascript
|
|
49
|
-
import { createClient } from '@supabase/supabase-js'
|
|
50
|
-
|
|
51
|
-
const supabaseUrl = process.env.SUPABASE_URL
|
|
52
|
-
const supabaseAnonKey = process.env.SUPABASE_ANON_KEY
|
|
53
|
-
|
|
54
|
-
const supabase = createClient(supabaseUrl, supabaseAnonKey)
|
|
55
|
-
```
|
|
56
|
-
|
|
57
|
-
### Environment Variables
|
|
58
|
-
|
|
59
|
-
Set these environment variables in your `.env` file:
|
|
60
|
-
|
|
61
|
-
```bash
|
|
62
|
-
SUPABASE_URL=https://xyzcompany.supabase.co
|
|
63
|
-
SUPABASE_ANON_KEY=your-anon-key-here
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
### Client Initialization with Options
|
|
67
|
-
|
|
68
|
-
```javascript
|
|
69
|
-
import { createClient } from '@supabase/supabase-js'
|
|
70
|
-
|
|
71
|
-
const supabase = createClient(supabaseUrl, supabaseAnonKey, {
|
|
72
|
-
auth: {
|
|
73
|
-
autoRefreshToken: true,
|
|
74
|
-
persistSession: true,
|
|
75
|
-
detectSessionInUrl: true
|
|
76
|
-
},
|
|
77
|
-
db: {
|
|
78
|
-
schema: 'public'
|
|
79
|
-
},
|
|
80
|
-
global: {
|
|
81
|
-
headers: { 'x-my-custom-header': 'my-app-name' }
|
|
82
|
-
}
|
|
83
|
-
})
|
|
84
|
-
```
|
|
85
|
-
|
|
86
|
-
### Server-Side Client (No Session Persistence)
|
|
87
|
-
|
|
88
|
-
```javascript
|
|
89
|
-
import { createClient } from '@supabase/supabase-js'
|
|
90
|
-
|
|
91
|
-
const supabase = createClient(supabaseUrl, supabaseAnonKey, {
|
|
92
|
-
auth: {
|
|
93
|
-
autoRefreshToken: false,
|
|
94
|
-
persistSession: false,
|
|
95
|
-
detectSessionInUrl: false
|
|
96
|
-
}
|
|
97
|
-
})
|
|
98
|
-
```
|
|
99
|
-
|
|
100
|
-
## Authentication
|
|
101
|
-
|
|
102
|
-
The Supabase SDK provides comprehensive authentication through `supabase.auth`.
|
|
103
|
-
|
|
104
|
-
### Sign Up with Email and Password
|
|
105
|
-
|
|
106
|
-
```javascript
|
|
107
|
-
const { data, error } = await supabase.auth.signUp({
|
|
108
|
-
email: 'user@example.com',
|
|
109
|
-
password: 'secure-password-123'
|
|
110
|
-
})
|
|
111
|
-
|
|
112
|
-
if (error) {
|
|
113
|
-
console.error('Error signing up:', error.message)
|
|
114
|
-
} else {
|
|
115
|
-
console.log('User created:', data.user)
|
|
116
|
-
}
|
|
117
|
-
```
|
|
118
|
-
|
|
119
|
-
### Sign Up with Additional User Metadata
|
|
120
|
-
|
|
121
|
-
```javascript
|
|
122
|
-
const { data, error } = await supabase.auth.signUp({
|
|
123
|
-
email: 'user@example.com',
|
|
124
|
-
password: 'secure-password-123',
|
|
125
|
-
options: {
|
|
126
|
-
data: {
|
|
127
|
-
first_name: 'John',
|
|
128
|
-
age: 27
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
})
|
|
132
|
-
```
|
|
133
|
-
|
|
134
|
-
### Sign In with Email and Password
|
|
135
|
-
|
|
136
|
-
```javascript
|
|
137
|
-
const { data, error } = await supabase.auth.signInWithPassword({
|
|
138
|
-
email: 'user@example.com',
|
|
139
|
-
password: 'secure-password-123'
|
|
140
|
-
})
|
|
141
|
-
|
|
142
|
-
if (error) {
|
|
143
|
-
console.error('Error signing in:', error.message)
|
|
144
|
-
} else {
|
|
145
|
-
console.log('User signed in:', data.user)
|
|
146
|
-
console.log('Session:', data.session)
|
|
147
|
-
}
|
|
148
|
-
```
|
|
149
|
-
|
|
150
|
-
### Sign In with OAuth
|
|
151
|
-
|
|
152
|
-
```javascript
|
|
153
|
-
const { data, error } = await supabase.auth.signInWithOAuth({
|
|
154
|
-
provider: 'google'
|
|
155
|
-
})
|
|
156
|
-
|
|
157
|
-
// Available providers: google, github, gitlab, bitbucket, azure,
|
|
158
|
-
// facebook, twitter, discord, slack, spotify, twitch, linkedin, etc.
|
|
159
|
-
```
|
|
160
|
-
|
|
161
|
-
### Sign In with OAuth and Options
|
|
162
|
-
|
|
163
|
-
```javascript
|
|
164
|
-
const { data, error } = await supabase.auth.signInWithOAuth({
|
|
165
|
-
provider: 'google',
|
|
166
|
-
options: {
|
|
167
|
-
redirectTo: 'https://example.com/auth/callback',
|
|
168
|
-
scopes: 'https://www.googleapis.com/auth/calendar',
|
|
169
|
-
queryParams: {
|
|
170
|
-
access_type: 'offline',
|
|
171
|
-
prompt: 'consent'
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
})
|
|
175
|
-
```
|
|
176
|
-
|
|
177
|
-
### Sign In with Magic Link (OTP)
|
|
178
|
-
|
|
179
|
-
```javascript
|
|
180
|
-
const { data, error } = await supabase.auth.signInWithOtp({
|
|
181
|
-
email: 'user@example.com',
|
|
182
|
-
options: {
|
|
183
|
-
emailRedirectTo: 'https://example.com/welcome'
|
|
184
|
-
}
|
|
185
|
-
})
|
|
186
|
-
```
|
|
187
|
-
|
|
188
|
-
### Sign In with Phone OTP
|
|
189
|
-
|
|
190
|
-
```javascript
|
|
191
|
-
// Send OTP
|
|
192
|
-
const { data, error } = await supabase.auth.signInWithOtp({
|
|
193
|
-
phone: '+1234567890'
|
|
194
|
-
})
|
|
195
|
-
|
|
196
|
-
// Verify OTP
|
|
197
|
-
const { data, error } = await supabase.auth.verifyOtp({
|
|
198
|
-
phone: '+1234567890',
|
|
199
|
-
token: '123456',
|
|
200
|
-
type: 'sms'
|
|
201
|
-
})
|
|
202
|
-
```
|
|
203
|
-
|
|
204
|
-
### Anonymous Sign In
|
|
205
|
-
|
|
206
|
-
```javascript
|
|
207
|
-
const { data, error } = await supabase.auth.signInAnonymously()
|
|
208
|
-
|
|
209
|
-
console.log('Anonymous user:', data.user)
|
|
210
|
-
```
|
|
211
|
-
|
|
212
|
-
### Get Current User
|
|
213
|
-
|
|
214
|
-
```javascript
|
|
215
|
-
const { data: { user } } = await supabase.auth.getUser()
|
|
216
|
-
|
|
217
|
-
if (user) {
|
|
218
|
-
console.log('Current user:', user)
|
|
219
|
-
} else {
|
|
220
|
-
console.log('No user logged in')
|
|
221
|
-
}
|
|
222
|
-
```
|
|
223
|
-
|
|
224
|
-
### Get Current Session
|
|
225
|
-
|
|
226
|
-
```javascript
|
|
227
|
-
const { data: { session } } = await supabase.auth.getSession()
|
|
228
|
-
|
|
229
|
-
if (session) {
|
|
230
|
-
console.log('Access token:', session.access_token)
|
|
231
|
-
console.log('Refresh token:', session.refresh_token)
|
|
232
|
-
console.log('Expires at:', session.expires_at)
|
|
233
|
-
}
|
|
234
|
-
```
|
|
235
|
-
|
|
236
|
-
### Update User
|
|
237
|
-
|
|
238
|
-
```javascript
|
|
239
|
-
const { data, error } = await supabase.auth.updateUser({
|
|
240
|
-
email: 'newemail@example.com',
|
|
241
|
-
password: 'new-password-456',
|
|
242
|
-
data: {
|
|
243
|
-
first_name: 'Jane',
|
|
244
|
-
age: 28
|
|
245
|
-
}
|
|
246
|
-
})
|
|
247
|
-
```
|
|
248
|
-
|
|
249
|
-
### Sign Out
|
|
250
|
-
|
|
251
|
-
```javascript
|
|
252
|
-
const { error } = await supabase.auth.signOut()
|
|
253
|
-
|
|
254
|
-
if (error) {
|
|
255
|
-
console.error('Error signing out:', error.message)
|
|
256
|
-
} else {
|
|
257
|
-
console.log('User signed out successfully')
|
|
258
|
-
}
|
|
259
|
-
```
|
|
260
|
-
|
|
261
|
-
### Password Reset
|
|
262
|
-
|
|
263
|
-
```javascript
|
|
264
|
-
// Send password reset email
|
|
265
|
-
const { data, error } = await supabase.auth.resetPasswordForEmail(
|
|
266
|
-
'user@example.com',
|
|
267
|
-
{
|
|
268
|
-
redirectTo: 'https://example.com/reset-password'
|
|
269
|
-
}
|
|
270
|
-
)
|
|
271
|
-
```
|
|
272
|
-
|
|
273
|
-
### Auth State Changes Listener
|
|
274
|
-
|
|
275
|
-
```javascript
|
|
276
|
-
const { data: { subscription } } = supabase.auth.onAuthStateChange(
|
|
277
|
-
(event, session) => {
|
|
278
|
-
console.log('Auth event:', event)
|
|
279
|
-
console.log('Session:', session)
|
|
280
|
-
|
|
281
|
-
if (event === 'SIGNED_IN') {
|
|
282
|
-
console.log('User signed in!')
|
|
283
|
-
}
|
|
284
|
-
if (event === 'SIGNED_OUT') {
|
|
285
|
-
console.log('User signed out!')
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
)
|
|
289
|
-
|
|
290
|
-
// Unsubscribe when done
|
|
291
|
-
subscription.unsubscribe()
|
|
292
|
-
```
|
|
293
|
-
|
|
294
|
-
### Refresh Session
|
|
295
|
-
|
|
296
|
-
```javascript
|
|
297
|
-
const { data, error } = await supabase.auth.refreshSession()
|
|
298
|
-
|
|
299
|
-
console.log('New session:', data.session)
|
|
300
|
-
```
|
|
301
|
-
|
|
302
|
-
### Exchange Auth Code (OAuth Flow)
|
|
303
|
-
|
|
304
|
-
```javascript
|
|
305
|
-
const { data, error } = await supabase.auth.exchangeCodeForSession(code)
|
|
306
|
-
```
|
|
307
|
-
|
|
308
|
-
## Database Queries
|
|
309
|
-
|
|
310
|
-
The Supabase SDK uses PostgREST for database operations with a chainable API.
|
|
311
|
-
|
|
312
|
-
### Select All Rows
|
|
313
|
-
|
|
314
|
-
```javascript
|
|
315
|
-
const { data, error } = await supabase
|
|
316
|
-
.from('countries')
|
|
317
|
-
.select('*')
|
|
318
|
-
|
|
319
|
-
if (error) {
|
|
320
|
-
console.error('Error fetching data:', error.message)
|
|
321
|
-
} else {
|
|
322
|
-
console.log('Countries:', data)
|
|
323
|
-
}
|
|
324
|
-
```
|
|
325
|
-
|
|
326
|
-
### Select Specific Columns
|
|
327
|
-
|
|
328
|
-
```javascript
|
|
329
|
-
const { data, error } = await supabase
|
|
330
|
-
.from('countries')
|
|
331
|
-
.select('id, name, capital')
|
|
332
|
-
```
|
|
333
|
-
|
|
334
|
-
### Select with Column Alias
|
|
335
|
-
|
|
336
|
-
```javascript
|
|
337
|
-
const { data, error } = await supabase
|
|
338
|
-
.from('countries')
|
|
339
|
-
.select('country_name:name, country_id:id')
|
|
340
|
-
```
|
|
341
|
-
|
|
342
|
-
### Select with Foreign Table Relations
|
|
343
|
-
|
|
344
|
-
```javascript
|
|
345
|
-
const { data, error } = await supabase
|
|
346
|
-
.from('countries')
|
|
347
|
-
.select(`
|
|
348
|
-
name,
|
|
349
|
-
cities (
|
|
350
|
-
name,
|
|
351
|
-
population
|
|
352
|
-
)
|
|
353
|
-
`)
|
|
354
|
-
```
|
|
355
|
-
|
|
356
|
-
### Select with Multiple Foreign Key References
|
|
357
|
-
|
|
358
|
-
```javascript
|
|
359
|
-
const { data, error } = await supabase
|
|
360
|
-
.from('messages')
|
|
361
|
-
.select(`
|
|
362
|
-
content,
|
|
363
|
-
from:sender_id(name),
|
|
364
|
-
to:receiver_id(name)
|
|
365
|
-
`)
|
|
366
|
-
```
|
|
367
|
-
|
|
368
|
-
### Select with Nested Foreign Tables
|
|
369
|
-
|
|
370
|
-
```javascript
|
|
371
|
-
const { data, error } = await supabase
|
|
372
|
-
.from('countries')
|
|
373
|
-
.select(`
|
|
374
|
-
name,
|
|
375
|
-
cities (
|
|
376
|
-
name,
|
|
377
|
-
buildings (
|
|
378
|
-
name,
|
|
379
|
-
floors
|
|
380
|
-
)
|
|
381
|
-
)
|
|
382
|
-
`)
|
|
383
|
-
```
|
|
384
|
-
|
|
385
|
-
### Select with Filters
|
|
386
|
-
|
|
387
|
-
```javascript
|
|
388
|
-
const { data, error } = await supabase
|
|
389
|
-
.from('countries')
|
|
390
|
-
.select('*')
|
|
391
|
-
.eq('name', 'United States')
|
|
392
|
-
```
|
|
393
|
-
|
|
394
|
-
### Filtering Operators
|
|
395
|
-
|
|
396
|
-
```javascript
|
|
397
|
-
// Equals
|
|
398
|
-
.eq('column', 'value')
|
|
399
|
-
|
|
400
|
-
// Not equals
|
|
401
|
-
.neq('column', 'value')
|
|
402
|
-
|
|
403
|
-
// Greater than
|
|
404
|
-
.gt('column', 10)
|
|
405
|
-
|
|
406
|
-
// Greater than or equal
|
|
407
|
-
.gte('column', 10)
|
|
408
|
-
|
|
409
|
-
// Less than
|
|
410
|
-
.lt('column', 10)
|
|
411
|
-
|
|
412
|
-
// Less than or equal
|
|
413
|
-
.lte('column', 10)
|
|
414
|
-
|
|
415
|
-
// Pattern matching (case-sensitive)
|
|
416
|
-
.like('column', '%pattern%')
|
|
417
|
-
|
|
418
|
-
// Pattern matching (case-insensitive)
|
|
419
|
-
.ilike('column', '%pattern%')
|
|
420
|
-
|
|
421
|
-
// Is null
|
|
422
|
-
.is('column', null)
|
|
423
|
-
|
|
424
|
-
// In array
|
|
425
|
-
.in('column', ['value1', 'value2', 'value3'])
|
|
426
|
-
|
|
427
|
-
// Contains (for arrays)
|
|
428
|
-
.contains('column', ['value1', 'value2'])
|
|
429
|
-
|
|
430
|
-
// Contained by (for arrays)
|
|
431
|
-
.containedBy('column', ['value1', 'value2', 'value3'])
|
|
432
|
-
|
|
433
|
-
// Range overlap
|
|
434
|
-
.overlaps('column', [start, end])
|
|
435
|
-
|
|
436
|
-
// Full text search
|
|
437
|
-
.textSearch('column', 'search terms')
|
|
438
|
-
```
|
|
439
|
-
|
|
440
|
-
### Multiple Filters (AND)
|
|
441
|
-
|
|
442
|
-
```javascript
|
|
443
|
-
const { data, error } = await supabase
|
|
444
|
-
.from('countries')
|
|
445
|
-
.select('*')
|
|
446
|
-
.eq('continent', 'Europe')
|
|
447
|
-
.gt('population', 1000000)
|
|
448
|
-
```
|
|
449
|
-
|
|
450
|
-
### OR Filters
|
|
451
|
-
|
|
452
|
-
```javascript
|
|
453
|
-
const { data, error } = await supabase
|
|
454
|
-
.from('countries')
|
|
455
|
-
.select('*')
|
|
456
|
-
.or('continent.eq.Europe,continent.eq.Asia')
|
|
457
|
-
```
|
|
458
|
-
|
|
459
|
-
### Filter on Foreign Table Columns
|
|
460
|
-
|
|
461
|
-
```javascript
|
|
462
|
-
const { data, error } = await supabase
|
|
463
|
-
.from('countries')
|
|
464
|
-
.select('name, cities(*)')
|
|
465
|
-
.eq('cities.name', 'Paris')
|
|
466
|
-
```
|
|
467
|
-
|
|
468
|
-
### Inner Join (Only Return Rows with Matches)
|
|
469
|
-
|
|
470
|
-
```javascript
|
|
471
|
-
const { data, error } = await supabase
|
|
472
|
-
.from('countries')
|
|
473
|
-
.select('name, cities!inner(name)')
|
|
474
|
-
.eq('cities.name', 'Paris')
|
|
475
|
-
```
|
|
476
|
-
|
|
477
|
-
### Ordering Results
|
|
478
|
-
|
|
479
|
-
```javascript
|
|
480
|
-
// Ascending order
|
|
481
|
-
const { data, error } = await supabase
|
|
482
|
-
.from('countries')
|
|
483
|
-
.select('*')
|
|
484
|
-
.order('name', { ascending: true })
|
|
485
|
-
|
|
486
|
-
// Descending order
|
|
487
|
-
const { data, error } = await supabase
|
|
488
|
-
.from('countries')
|
|
489
|
-
.select('*')
|
|
490
|
-
.order('population', { ascending: false })
|
|
491
|
-
```
|
|
492
|
-
|
|
493
|
-
### Ordering with Nulls
|
|
494
|
-
|
|
495
|
-
```javascript
|
|
496
|
-
const { data, error } = await supabase
|
|
497
|
-
.from('countries')
|
|
498
|
-
.select('*')
|
|
499
|
-
.order('name', { ascending: true, nullsFirst: false })
|
|
500
|
-
```
|
|
501
|
-
|
|
502
|
-
### Order Foreign Table Columns
|
|
503
|
-
|
|
504
|
-
```javascript
|
|
505
|
-
const { data, error } = await supabase
|
|
506
|
-
.from('countries')
|
|
507
|
-
.select('name, cities(name)')
|
|
508
|
-
.order('name', { foreignTable: 'cities' })
|
|
509
|
-
```
|
|
510
|
-
|
|
511
|
-
### Limit Results
|
|
512
|
-
|
|
513
|
-
```javascript
|
|
514
|
-
const { data, error } = await supabase
|
|
515
|
-
.from('countries')
|
|
516
|
-
.select('*')
|
|
517
|
-
.limit(10)
|
|
518
|
-
```
|
|
519
|
-
|
|
520
|
-
### Limit Foreign Table Rows
|
|
521
|
-
|
|
522
|
-
```javascript
|
|
523
|
-
const { data, error } = await supabase
|
|
524
|
-
.from('countries')
|
|
525
|
-
.select('name, cities(name)')
|
|
526
|
-
.limit(5, { foreignTable: 'cities' })
|
|
527
|
-
```
|
|
528
|
-
|
|
529
|
-
### Pagination with Range
|
|
530
|
-
|
|
531
|
-
```javascript
|
|
532
|
-
// Get rows 0-9
|
|
533
|
-
const { data, error } = await supabase
|
|
534
|
-
.from('countries')
|
|
535
|
-
.select('*')
|
|
536
|
-
.range(0, 9)
|
|
537
|
-
|
|
538
|
-
// Get rows 10-19
|
|
539
|
-
const { data, error } = await supabase
|
|
540
|
-
.from('countries')
|
|
541
|
-
.select('*')
|
|
542
|
-
.range(10, 19)
|
|
543
|
-
```
|
|
544
|
-
|
|
545
|
-
### Get Single Row
|
|
546
|
-
|
|
547
|
-
```javascript
|
|
548
|
-
const { data, error } = await supabase
|
|
549
|
-
.from('countries')
|
|
550
|
-
.select('*')
|
|
551
|
-
.eq('id', 1)
|
|
552
|
-
.single()
|
|
553
|
-
|
|
554
|
-
// Returns single object instead of array
|
|
555
|
-
```
|
|
556
|
-
|
|
557
|
-
### Maybe Single (No Error if Not Found)
|
|
558
|
-
|
|
559
|
-
```javascript
|
|
560
|
-
const { data, error } = await supabase
|
|
561
|
-
.from('countries')
|
|
562
|
-
.select('*')
|
|
563
|
-
.eq('id', 1)
|
|
564
|
-
.maybeSingle()
|
|
565
|
-
```
|
|
566
|
-
|
|
567
|
-
### Count Rows
|
|
568
|
-
|
|
569
|
-
```javascript
|
|
570
|
-
const { count, error } = await supabase
|
|
571
|
-
.from('countries')
|
|
572
|
-
.select('*', { count: 'exact', head: true })
|
|
573
|
-
|
|
574
|
-
console.log('Total countries:', count)
|
|
575
|
-
```
|
|
576
|
-
|
|
577
|
-
### Query JSON Columns
|
|
578
|
-
|
|
579
|
-
```javascript
|
|
580
|
-
const { data, error } = await supabase
|
|
581
|
-
.from('users')
|
|
582
|
-
.select('id, name, address->city, address->country')
|
|
583
|
-
```
|
|
584
|
-
|
|
585
|
-
### Query Array Columns
|
|
586
|
-
|
|
587
|
-
```javascript
|
|
588
|
-
const { data, error } = await supabase
|
|
589
|
-
.from('users')
|
|
590
|
-
.select('*')
|
|
591
|
-
.contains('tags', ['developer', 'designer'])
|
|
592
|
-
```
|
|
593
|
-
|
|
594
|
-
### Use Different Schema
|
|
595
|
-
|
|
596
|
-
```javascript
|
|
597
|
-
const { data, error } = await supabase
|
|
598
|
-
.schema('private')
|
|
599
|
-
.from('employees')
|
|
600
|
-
.select('*')
|
|
601
|
-
```
|
|
602
|
-
|
|
603
|
-
## Insert Data
|
|
604
|
-
|
|
605
|
-
### Insert Single Row
|
|
606
|
-
|
|
607
|
-
```javascript
|
|
608
|
-
const { data, error } = await supabase
|
|
609
|
-
.from('countries')
|
|
610
|
-
.insert({
|
|
611
|
-
name: 'Canada',
|
|
612
|
-
capital: 'Ottawa',
|
|
613
|
-
population: 38000000
|
|
614
|
-
})
|
|
615
|
-
```
|
|
616
|
-
|
|
617
|
-
### Insert Single Row and Return Data
|
|
618
|
-
|
|
619
|
-
```javascript
|
|
620
|
-
const { data, error } = await supabase
|
|
621
|
-
.from('countries')
|
|
622
|
-
.insert({
|
|
623
|
-
name: 'Canada',
|
|
624
|
-
capital: 'Ottawa'
|
|
625
|
-
})
|
|
626
|
-
.select()
|
|
627
|
-
|
|
628
|
-
console.log('Inserted row:', data)
|
|
629
|
-
```
|
|
630
|
-
|
|
631
|
-
### Insert Multiple Rows
|
|
632
|
-
|
|
633
|
-
```javascript
|
|
634
|
-
const { data, error } = await supabase
|
|
635
|
-
.from('countries')
|
|
636
|
-
.insert([
|
|
637
|
-
{ name: 'Canada', capital: 'Ottawa' },
|
|
638
|
-
{ name: 'Mexico', capital: 'Mexico City' },
|
|
639
|
-
{ name: 'Brazil', capital: 'Brasilia' }
|
|
640
|
-
])
|
|
641
|
-
.select()
|
|
642
|
-
```
|
|
643
|
-
|
|
644
|
-
### Insert and Return Specific Columns
|
|
645
|
-
|
|
646
|
-
```javascript
|
|
647
|
-
const { data, error } = await supabase
|
|
648
|
-
.from('countries')
|
|
649
|
-
.insert({ name: 'Canada', capital: 'Ottawa' })
|
|
650
|
-
.select('id, name')
|
|
651
|
-
```
|
|
652
|
-
|
|
653
|
-
## Update Data
|
|
654
|
-
|
|
655
|
-
### Update Matching Rows
|
|
656
|
-
|
|
657
|
-
```javascript
|
|
658
|
-
const { data, error } = await supabase
|
|
659
|
-
.from('countries')
|
|
660
|
-
.update({ capital: 'New Capital' })
|
|
661
|
-
.eq('name', 'Canada')
|
|
662
|
-
```
|
|
663
|
-
|
|
664
|
-
### Update with Multiple Filters
|
|
665
|
-
|
|
666
|
-
```javascript
|
|
667
|
-
const { data, error } = await supabase
|
|
668
|
-
.from('countries')
|
|
669
|
-
.update({ population: 39000000 })
|
|
670
|
-
.eq('name', 'Canada')
|
|
671
|
-
.lt('population', 40000000)
|
|
672
|
-
```
|
|
673
|
-
|
|
674
|
-
### Update and Return Data
|
|
675
|
-
|
|
676
|
-
```javascript
|
|
677
|
-
const { data, error } = await supabase
|
|
678
|
-
.from('countries')
|
|
679
|
-
.update({ capital: 'New Capital' })
|
|
680
|
-
.eq('id', 1)
|
|
681
|
-
.select()
|
|
682
|
-
```
|
|
683
|
-
|
|
684
|
-
### Update JSON Column
|
|
685
|
-
|
|
686
|
-
```javascript
|
|
687
|
-
const { data, error } = await supabase
|
|
688
|
-
.from('users')
|
|
689
|
-
.update({
|
|
690
|
-
settings: { theme: 'dark', language: 'en' }
|
|
691
|
-
})
|
|
692
|
-
.eq('id', 1)
|
|
693
|
-
```
|
|
694
|
-
|
|
695
|
-
## Upsert Data
|
|
696
|
-
|
|
697
|
-
### Upsert (Insert or Update)
|
|
698
|
-
|
|
699
|
-
```javascript
|
|
700
|
-
const { data, error } = await supabase
|
|
701
|
-
.from('countries')
|
|
702
|
-
.upsert({
|
|
703
|
-
id: 1,
|
|
704
|
-
name: 'Canada',
|
|
705
|
-
capital: 'Ottawa'
|
|
706
|
-
})
|
|
707
|
-
```
|
|
708
|
-
|
|
709
|
-
### Upsert Multiple Rows
|
|
710
|
-
|
|
711
|
-
```javascript
|
|
712
|
-
const { data, error } = await supabase
|
|
713
|
-
.from('countries')
|
|
714
|
-
.upsert([
|
|
715
|
-
{ id: 1, name: 'Canada', capital: 'Ottawa' },
|
|
716
|
-
{ id: 2, name: 'Mexico', capital: 'Mexico City' }
|
|
717
|
-
])
|
|
718
|
-
```
|
|
719
|
-
|
|
720
|
-
### Upsert with Conflict Resolution
|
|
721
|
-
|
|
722
|
-
```javascript
|
|
723
|
-
const { data, error } = await supabase
|
|
724
|
-
.from('countries')
|
|
725
|
-
.upsert(
|
|
726
|
-
{ id: 1, name: 'Canada' },
|
|
727
|
-
{ onConflict: 'id' }
|
|
728
|
-
)
|
|
729
|
-
```
|
|
730
|
-
|
|
731
|
-
### Upsert and Ignore Duplicates
|
|
732
|
-
|
|
733
|
-
```javascript
|
|
734
|
-
const { data, error } = await supabase
|
|
735
|
-
.from('countries')
|
|
736
|
-
.upsert(
|
|
737
|
-
{ name: 'Canada', capital: 'Ottawa' },
|
|
738
|
-
{ onConflict: 'name', ignoreDuplicates: true }
|
|
739
|
-
)
|
|
740
|
-
```
|
|
741
|
-
|
|
742
|
-
## Delete Data
|
|
743
|
-
|
|
744
|
-
### Delete Matching Rows
|
|
745
|
-
|
|
746
|
-
```javascript
|
|
747
|
-
const { error } = await supabase
|
|
748
|
-
.from('countries')
|
|
749
|
-
.delete()
|
|
750
|
-
.eq('name', 'Canada')
|
|
751
|
-
```
|
|
752
|
-
|
|
753
|
-
### Delete with Multiple Filters
|
|
754
|
-
|
|
755
|
-
```javascript
|
|
756
|
-
const { error } = await supabase
|
|
757
|
-
.from('countries')
|
|
758
|
-
.delete()
|
|
759
|
-
.eq('continent', 'Europe')
|
|
760
|
-
.lt('population', 100000)
|
|
761
|
-
```
|
|
762
|
-
|
|
763
|
-
### Delete and Return Data
|
|
764
|
-
|
|
765
|
-
```javascript
|
|
766
|
-
const { data, error } = await supabase
|
|
767
|
-
.from('countries')
|
|
768
|
-
.delete()
|
|
769
|
-
.eq('id', 1)
|
|
770
|
-
.select()
|
|
771
|
-
```
|
|
772
|
-
|
|
773
|
-
## RPC (Call Postgres Functions)
|
|
774
|
-
|
|
775
|
-
### Call Function Without Parameters
|
|
776
|
-
|
|
777
|
-
```javascript
|
|
778
|
-
const { data, error } = await supabase.rpc('hello_world')
|
|
779
|
-
|
|
780
|
-
console.log('Function result:', data)
|
|
781
|
-
```
|
|
782
|
-
|
|
783
|
-
### Call Function with Parameters
|
|
784
|
-
|
|
785
|
-
```javascript
|
|
786
|
-
const { data, error } = await supabase.rpc('add_numbers', {
|
|
787
|
-
a: 5,
|
|
788
|
-
b: 10
|
|
789
|
-
})
|
|
790
|
-
|
|
791
|
-
console.log('Sum:', data)
|
|
792
|
-
```
|
|
793
|
-
|
|
794
|
-
### Call Function with Array Parameter
|
|
795
|
-
|
|
796
|
-
```javascript
|
|
797
|
-
const { data, error } = await supabase.rpc('add_one_each', {
|
|
798
|
-
arr: [1, 2, 3, 4, 5]
|
|
799
|
-
})
|
|
800
|
-
|
|
801
|
-
console.log('Result:', data)
|
|
802
|
-
```
|
|
803
|
-
|
|
804
|
-
### Chain Filters with RPC
|
|
805
|
-
|
|
806
|
-
```javascript
|
|
807
|
-
const { data, error } = await supabase
|
|
808
|
-
.rpc('get_countries')
|
|
809
|
-
.eq('continent', 'Europe')
|
|
810
|
-
.order('population', { ascending: false })
|
|
811
|
-
.limit(10)
|
|
812
|
-
```
|
|
813
|
-
|
|
814
|
-
### RPC with Single Result
|
|
815
|
-
|
|
816
|
-
```javascript
|
|
817
|
-
const { data, error } = await supabase
|
|
818
|
-
.rpc('get_country_by_id', { country_id: 1 })
|
|
819
|
-
.single()
|
|
820
|
-
```
|
|
821
|
-
|
|
822
|
-
### Call Function on Read Replica
|
|
823
|
-
|
|
824
|
-
```javascript
|
|
825
|
-
const { data, error } = await supabase.rpc(
|
|
826
|
-
'get_data',
|
|
827
|
-
{},
|
|
828
|
-
{ get: true }
|
|
829
|
-
)
|
|
830
|
-
```
|
|
831
|
-
|
|
832
|
-
## Realtime Subscriptions
|
|
833
|
-
|
|
834
|
-
The Supabase SDK provides realtime functionality through channels.
|
|
835
|
-
|
|
836
|
-
### Subscribe to Database Changes
|
|
837
|
-
|
|
838
|
-
```javascript
|
|
839
|
-
const channel = supabase
|
|
840
|
-
.channel('db-changes')
|
|
841
|
-
.on(
|
|
842
|
-
'postgres_changes',
|
|
843
|
-
{
|
|
844
|
-
event: '*',
|
|
845
|
-
schema: 'public',
|
|
846
|
-
table: 'countries'
|
|
847
|
-
},
|
|
848
|
-
(payload) => {
|
|
849
|
-
console.log('Change received!', payload)
|
|
850
|
-
}
|
|
851
|
-
)
|
|
852
|
-
.subscribe()
|
|
853
|
-
```
|
|
854
|
-
|
|
855
|
-
### Subscribe to INSERT Events
|
|
856
|
-
|
|
857
|
-
```javascript
|
|
858
|
-
const channel = supabase
|
|
859
|
-
.channel('db-inserts')
|
|
860
|
-
.on(
|
|
861
|
-
'postgres_changes',
|
|
862
|
-
{
|
|
863
|
-
event: 'INSERT',
|
|
864
|
-
schema: 'public',
|
|
865
|
-
table: 'countries'
|
|
866
|
-
},
|
|
867
|
-
(payload) => {
|
|
868
|
-
console.log('New record:', payload.new)
|
|
869
|
-
}
|
|
870
|
-
)
|
|
871
|
-
.subscribe()
|
|
872
|
-
```
|
|
873
|
-
|
|
874
|
-
### Subscribe to UPDATE Events
|
|
875
|
-
|
|
876
|
-
```javascript
|
|
877
|
-
const channel = supabase
|
|
878
|
-
.channel('db-updates')
|
|
879
|
-
.on(
|
|
880
|
-
'postgres_changes',
|
|
881
|
-
{
|
|
882
|
-
event: 'UPDATE',
|
|
883
|
-
schema: 'public',
|
|
884
|
-
table: 'countries'
|
|
885
|
-
},
|
|
886
|
-
(payload) => {
|
|
887
|
-
console.log('Old record:', payload.old)
|
|
888
|
-
console.log('New record:', payload.new)
|
|
889
|
-
}
|
|
890
|
-
)
|
|
891
|
-
.subscribe()
|
|
892
|
-
```
|
|
893
|
-
|
|
894
|
-
### Subscribe to DELETE Events
|
|
895
|
-
|
|
896
|
-
```javascript
|
|
897
|
-
const channel = supabase
|
|
898
|
-
.channel('db-deletes')
|
|
899
|
-
.on(
|
|
900
|
-
'postgres_changes',
|
|
901
|
-
{
|
|
902
|
-
event: 'DELETE',
|
|
903
|
-
schema: 'public',
|
|
904
|
-
table: 'countries'
|
|
905
|
-
},
|
|
906
|
-
(payload) => {
|
|
907
|
-
console.log('Deleted record:', payload.old)
|
|
908
|
-
}
|
|
909
|
-
)
|
|
910
|
-
.subscribe()
|
|
911
|
-
```
|
|
912
|
-
|
|
913
|
-
### Subscribe with Row-Level Filter
|
|
914
|
-
|
|
915
|
-
```javascript
|
|
916
|
-
const channel = supabase
|
|
917
|
-
.channel('specific-row')
|
|
918
|
-
.on(
|
|
919
|
-
'postgres_changes',
|
|
920
|
-
{
|
|
921
|
-
event: '*',
|
|
922
|
-
schema: 'public',
|
|
923
|
-
table: 'countries',
|
|
924
|
-
filter: 'id=eq.1'
|
|
925
|
-
},
|
|
926
|
-
(payload) => {
|
|
927
|
-
console.log('Change to specific row:', payload)
|
|
928
|
-
}
|
|
929
|
-
)
|
|
930
|
-
.subscribe()
|
|
931
|
-
```
|
|
932
|
-
|
|
933
|
-
### Subscribe to Multiple Tables
|
|
934
|
-
|
|
935
|
-
```javascript
|
|
936
|
-
const channel = supabase
|
|
937
|
-
.channel('multi-table')
|
|
938
|
-
.on(
|
|
939
|
-
'postgres_changes',
|
|
940
|
-
{ event: '*', schema: 'public', table: 'countries' },
|
|
941
|
-
handleCountryChange
|
|
942
|
-
)
|
|
943
|
-
.on(
|
|
944
|
-
'postgres_changes',
|
|
945
|
-
{ event: '*', schema: 'public', table: 'cities' },
|
|
946
|
-
handleCityChange
|
|
947
|
-
)
|
|
948
|
-
.subscribe()
|
|
949
|
-
```
|
|
950
|
-
|
|
951
|
-
### Unsubscribe from Changes
|
|
952
|
-
|
|
953
|
-
```javascript
|
|
954
|
-
const channel = supabase.channel('my-channel')
|
|
955
|
-
|
|
956
|
-
// ... set up subscriptions
|
|
957
|
-
|
|
958
|
-
// Later, unsubscribe
|
|
959
|
-
await supabase.removeChannel(channel)
|
|
960
|
-
```
|
|
961
|
-
|
|
962
|
-
### Broadcast Messages
|
|
963
|
-
|
|
964
|
-
```javascript
|
|
965
|
-
// Send broadcast
|
|
966
|
-
const channel = supabase.channel('room-1')
|
|
967
|
-
|
|
968
|
-
channel.subscribe((status) => {
|
|
969
|
-
if (status === 'SUBSCRIBED') {
|
|
970
|
-
channel.send({
|
|
971
|
-
type: 'broadcast',
|
|
972
|
-
event: 'cursor-pos',
|
|
973
|
-
payload: { x: 100, y: 200 }
|
|
974
|
-
})
|
|
975
|
-
}
|
|
976
|
-
})
|
|
977
|
-
|
|
978
|
-
// Receive broadcast
|
|
979
|
-
channel.on('broadcast', { event: 'cursor-pos' }, (payload) => {
|
|
980
|
-
console.log('Cursor position:', payload)
|
|
981
|
-
})
|
|
982
|
-
```
|
|
983
|
-
|
|
984
|
-
### Presence Tracking
|
|
985
|
-
|
|
986
|
-
```javascript
|
|
987
|
-
const channel = supabase.channel('room-1')
|
|
988
|
-
|
|
989
|
-
// Track user presence
|
|
990
|
-
channel.on('presence', { event: 'sync' }, () => {
|
|
991
|
-
const state = channel.presenceState()
|
|
992
|
-
console.log('Online users:', state)
|
|
993
|
-
})
|
|
994
|
-
|
|
995
|
-
channel.on('presence', { event: 'join' }, ({ key, newPresences }) => {
|
|
996
|
-
console.log('User joined:', newPresences)
|
|
997
|
-
})
|
|
998
|
-
|
|
999
|
-
channel.on('presence', { event: 'leave' }, ({ key, leftPresences }) => {
|
|
1000
|
-
console.log('User left:', leftPresences)
|
|
1001
|
-
})
|
|
1002
|
-
|
|
1003
|
-
// Subscribe and track current user
|
|
1004
|
-
channel.subscribe(async (status) => {
|
|
1005
|
-
if (status === 'SUBSCRIBED') {
|
|
1006
|
-
await channel.track({
|
|
1007
|
-
user_id: 1,
|
|
1008
|
-
online_at: new Date().toISOString()
|
|
1009
|
-
})
|
|
1010
|
-
}
|
|
1011
|
-
})
|
|
1012
|
-
```
|
|
1013
|
-
|
|
1014
|
-
### Untrack Presence
|
|
1015
|
-
|
|
1016
|
-
```javascript
|
|
1017
|
-
await channel.untrack()
|
|
1018
|
-
```
|
|
1019
|
-
|
|
1020
|
-
### Channel Status Callbacks
|
|
1021
|
-
|
|
1022
|
-
```javascript
|
|
1023
|
-
const channel = supabase
|
|
1024
|
-
.channel('my-channel')
|
|
1025
|
-
.subscribe((status, err) => {
|
|
1026
|
-
if (status === 'SUBSCRIBED') {
|
|
1027
|
-
console.log('Connected!')
|
|
1028
|
-
}
|
|
1029
|
-
if (status === 'CHANNEL_ERROR') {
|
|
1030
|
-
console.error('Connection error:', err)
|
|
1031
|
-
}
|
|
1032
|
-
if (status === 'TIMED_OUT') {
|
|
1033
|
-
console.log('Connection timed out')
|
|
1034
|
-
}
|
|
1035
|
-
if (status === 'CLOSED') {
|
|
1036
|
-
console.log('Channel closed')
|
|
1037
|
-
}
|
|
1038
|
-
})
|
|
1039
|
-
```
|
|
1040
|
-
|
|
1041
|
-
## Storage
|
|
1042
|
-
|
|
1043
|
-
The Supabase SDK provides file storage through `supabase.storage`.
|
|
1044
|
-
|
|
1045
|
-
### List Buckets
|
|
1046
|
-
|
|
1047
|
-
```javascript
|
|
1048
|
-
const { data, error } = await supabase
|
|
1049
|
-
.storage
|
|
1050
|
-
.listBuckets()
|
|
1051
|
-
|
|
1052
|
-
console.log('Buckets:', data)
|
|
1053
|
-
```
|
|
1054
|
-
|
|
1055
|
-
### Get Bucket
|
|
1056
|
-
|
|
1057
|
-
```javascript
|
|
1058
|
-
const { data, error } = await supabase
|
|
1059
|
-
.storage
|
|
1060
|
-
.getBucket('avatars')
|
|
1061
|
-
|
|
1062
|
-
console.log('Bucket:', data)
|
|
1063
|
-
```
|
|
1064
|
-
|
|
1065
|
-
### Create Bucket
|
|
1066
|
-
|
|
1067
|
-
```javascript
|
|
1068
|
-
const { data, error } = await supabase
|
|
1069
|
-
.storage
|
|
1070
|
-
.createBucket('avatars', {
|
|
1071
|
-
public: false,
|
|
1072
|
-
fileSizeLimit: 1024000
|
|
1073
|
-
})
|
|
1074
|
-
```
|
|
1075
|
-
|
|
1076
|
-
### Update Bucket
|
|
1077
|
-
|
|
1078
|
-
```javascript
|
|
1079
|
-
const { data, error } = await supabase
|
|
1080
|
-
.storage
|
|
1081
|
-
.updateBucket('avatars', {
|
|
1082
|
-
public: true
|
|
1083
|
-
})
|
|
1084
|
-
```
|
|
1085
|
-
|
|
1086
|
-
### Delete Bucket
|
|
1087
|
-
|
|
1088
|
-
```javascript
|
|
1089
|
-
// Must empty bucket first
|
|
1090
|
-
const { data: files } = await supabase
|
|
1091
|
-
.storage
|
|
1092
|
-
.from('avatars')
|
|
1093
|
-
.list()
|
|
1094
|
-
|
|
1095
|
-
await supabase
|
|
1096
|
-
.storage
|
|
1097
|
-
.from('avatars')
|
|
1098
|
-
.remove(files.map(file => file.name))
|
|
1099
|
-
|
|
1100
|
-
// Then delete bucket
|
|
1101
|
-
const { data, error } = await supabase
|
|
1102
|
-
.storage
|
|
1103
|
-
.deleteBucket('avatars')
|
|
1104
|
-
```
|
|
1105
|
-
|
|
1106
|
-
### Empty Bucket
|
|
1107
|
-
|
|
1108
|
-
```javascript
|
|
1109
|
-
const { data, error } = await supabase
|
|
1110
|
-
.storage
|
|
1111
|
-
.emptyBucket('avatars')
|
|
1112
|
-
```
|
|
1113
|
-
|
|
1114
|
-
### Upload File
|
|
1115
|
-
|
|
1116
|
-
```javascript
|
|
1117
|
-
const file = event.target.files[0]
|
|
1118
|
-
|
|
1119
|
-
const { data, error } = await supabase
|
|
1120
|
-
.storage
|
|
1121
|
-
.from('avatars')
|
|
1122
|
-
.upload('public/avatar1.png', file, {
|
|
1123
|
-
cacheControl: '3600',
|
|
1124
|
-
upsert: false
|
|
1125
|
-
})
|
|
1126
|
-
|
|
1127
|
-
if (error) {
|
|
1128
|
-
console.error('Error uploading:', error.message)
|
|
1129
|
-
} else {
|
|
1130
|
-
console.log('File path:', data.path)
|
|
1131
|
-
}
|
|
1132
|
-
```
|
|
1133
|
-
|
|
1134
|
-
### Upload File with Content Type
|
|
1135
|
-
|
|
1136
|
-
```javascript
|
|
1137
|
-
const { data, error } = await supabase
|
|
1138
|
-
.storage
|
|
1139
|
-
.from('avatars')
|
|
1140
|
-
.upload('folder/file.pdf', file, {
|
|
1141
|
-
contentType: 'application/pdf'
|
|
1142
|
-
})
|
|
1143
|
-
```
|
|
1144
|
-
|
|
1145
|
-
### Upload and Overwrite Existing File
|
|
1146
|
-
|
|
1147
|
-
```javascript
|
|
1148
|
-
const { data, error } = await supabase
|
|
1149
|
-
.storage
|
|
1150
|
-
.from('avatars')
|
|
1151
|
-
.upload('public/avatar1.png', file, {
|
|
1152
|
-
upsert: true
|
|
1153
|
-
})
|
|
1154
|
-
```
|
|
1155
|
-
|
|
1156
|
-
### Upload from Base64
|
|
1157
|
-
|
|
1158
|
-
```javascript
|
|
1159
|
-
const base64 = 'data:image/png;base64,iVBORw0KGgoAAAANS...'
|
|
1160
|
-
|
|
1161
|
-
const { data, error } = await supabase
|
|
1162
|
-
.storage
|
|
1163
|
-
.from('avatars')
|
|
1164
|
-
.upload('public/avatar1.png', base64, {
|
|
1165
|
-
contentType: 'image/png'
|
|
1166
|
-
})
|
|
1167
|
-
```
|
|
1168
|
-
|
|
1169
|
-
### Download File
|
|
1170
|
-
|
|
1171
|
-
```javascript
|
|
1172
|
-
const { data, error } = await supabase
|
|
1173
|
-
.storage
|
|
1174
|
-
.from('avatars')
|
|
1175
|
-
.download('folder/avatar1.png')
|
|
1176
|
-
|
|
1177
|
-
if (error) {
|
|
1178
|
-
console.error('Error downloading:', error.message)
|
|
1179
|
-
} else {
|
|
1180
|
-
// data is a Blob
|
|
1181
|
-
const url = URL.createObjectURL(data)
|
|
1182
|
-
console.log('File URL:', url)
|
|
1183
|
-
}
|
|
1184
|
-
```
|
|
1185
|
-
|
|
1186
|
-
### Get Public URL
|
|
1187
|
-
|
|
1188
|
-
```javascript
|
|
1189
|
-
const { data } = supabase
|
|
1190
|
-
.storage
|
|
1191
|
-
.from('avatars')
|
|
1192
|
-
.getPublicUrl('folder/avatar1.png')
|
|
1193
|
-
|
|
1194
|
-
console.log('Public URL:', data.publicUrl)
|
|
1195
|
-
```
|
|
1196
|
-
|
|
1197
|
-
### Get Public URL with Transform
|
|
1198
|
-
|
|
1199
|
-
```javascript
|
|
1200
|
-
const { data } = supabase
|
|
1201
|
-
.storage
|
|
1202
|
-
.from('avatars')
|
|
1203
|
-
.getPublicUrl('folder/avatar1.png', {
|
|
1204
|
-
transform: {
|
|
1205
|
-
width: 200,
|
|
1206
|
-
height: 200,
|
|
1207
|
-
resize: 'cover'
|
|
1208
|
-
}
|
|
1209
|
-
})
|
|
1210
|
-
```
|
|
1211
|
-
|
|
1212
|
-
### Create Signed URL
|
|
1213
|
-
|
|
1214
|
-
```javascript
|
|
1215
|
-
const { data, error } = await supabase
|
|
1216
|
-
.storage
|
|
1217
|
-
.from('avatars')
|
|
1218
|
-
.createSignedUrl('folder/avatar1.png', 3600)
|
|
1219
|
-
|
|
1220
|
-
console.log('Signed URL:', data.signedUrl)
|
|
1221
|
-
console.log('Expires at:', new Date(Date.now() + 3600 * 1000))
|
|
1222
|
-
```
|
|
1223
|
-
|
|
1224
|
-
### Create Signed URLs (Multiple)
|
|
1225
|
-
|
|
1226
|
-
```javascript
|
|
1227
|
-
const { data, error } = await supabase
|
|
1228
|
-
.storage
|
|
1229
|
-
.from('avatars')
|
|
1230
|
-
.createSignedUrls(['folder/avatar1.png', 'folder/avatar2.png'], 3600)
|
|
1231
|
-
|
|
1232
|
-
console.log('Signed URLs:', data)
|
|
1233
|
-
```
|
|
1234
|
-
|
|
1235
|
-
### Create Signed Upload URL
|
|
1236
|
-
|
|
1237
|
-
```javascript
|
|
1238
|
-
const { data, error } = await supabase
|
|
1239
|
-
.storage
|
|
1240
|
-
.from('avatars')
|
|
1241
|
-
.createSignedUploadUrl('folder/avatar1.png')
|
|
1242
|
-
|
|
1243
|
-
console.log('Upload URL:', data.signedUrl)
|
|
1244
|
-
console.log('Upload token:', data.token)
|
|
1245
|
-
```
|
|
1246
|
-
|
|
1247
|
-
### Upload to Signed URL
|
|
1248
|
-
|
|
1249
|
-
```javascript
|
|
1250
|
-
const { data: signedData } = await supabase
|
|
1251
|
-
.storage
|
|
1252
|
-
.from('avatars')
|
|
1253
|
-
.createSignedUploadUrl('folder/avatar1.png')
|
|
1254
|
-
|
|
1255
|
-
const { data, error } = await supabase
|
|
1256
|
-
.storage
|
|
1257
|
-
.from('avatars')
|
|
1258
|
-
.uploadToSignedUrl(signedData.path, signedData.token, file)
|
|
1259
|
-
```
|
|
1260
|
-
|
|
1261
|
-
### List Files in Bucket
|
|
1262
|
-
|
|
1263
|
-
```javascript
|
|
1264
|
-
const { data, error } = await supabase
|
|
1265
|
-
.storage
|
|
1266
|
-
.from('avatars')
|
|
1267
|
-
.list('folder', {
|
|
1268
|
-
limit: 100,
|
|
1269
|
-
offset: 0,
|
|
1270
|
-
sortBy: { column: 'name', order: 'asc' }
|
|
1271
|
-
})
|
|
1272
|
-
|
|
1273
|
-
console.log('Files:', data)
|
|
1274
|
-
```
|
|
1275
|
-
|
|
1276
|
-
### Search Files
|
|
1277
|
-
|
|
1278
|
-
```javascript
|
|
1279
|
-
const { data, error } = await supabase
|
|
1280
|
-
.storage
|
|
1281
|
-
.from('avatars')
|
|
1282
|
-
.list('folder', {
|
|
1283
|
-
search: 'avatar'
|
|
1284
|
-
})
|
|
1285
|
-
```
|
|
1286
|
-
|
|
1287
|
-
### Move File
|
|
1288
|
-
|
|
1289
|
-
```javascript
|
|
1290
|
-
const { data, error } = await supabase
|
|
1291
|
-
.storage
|
|
1292
|
-
.from('avatars')
|
|
1293
|
-
.move('old/path/avatar.png', 'new/path/avatar.png')
|
|
1294
|
-
```
|
|
1295
|
-
|
|
1296
|
-
### Copy File
|
|
1297
|
-
|
|
1298
|
-
```javascript
|
|
1299
|
-
const { data, error } = await supabase
|
|
1300
|
-
.storage
|
|
1301
|
-
.from('avatars')
|
|
1302
|
-
.copy('original/avatar.png', 'backup/avatar.png')
|
|
1303
|
-
```
|
|
1304
|
-
|
|
1305
|
-
### Delete Files
|
|
1306
|
-
|
|
1307
|
-
```javascript
|
|
1308
|
-
const { data, error } = await supabase
|
|
1309
|
-
.storage
|
|
1310
|
-
.from('avatars')
|
|
1311
|
-
.remove(['folder/avatar1.png', 'folder/avatar2.png'])
|
|
1312
|
-
```
|
|
1313
|
-
|
|
1314
|
-
### Get File Metadata
|
|
1315
|
-
|
|
1316
|
-
```javascript
|
|
1317
|
-
const { data, error } = await supabase
|
|
1318
|
-
.storage
|
|
1319
|
-
.from('avatars')
|
|
1320
|
-
.list('folder', {
|
|
1321
|
-
limit: 1
|
|
1322
|
-
})
|
|
1323
|
-
|
|
1324
|
-
console.log('Metadata:', data[0])
|
|
1325
|
-
```
|
|
1326
|
-
|
|
1327
|
-
## Edge Functions
|
|
1328
|
-
|
|
1329
|
-
The Supabase SDK allows invoking Edge Functions.
|
|
1330
|
-
|
|
1331
|
-
### Invoke Function
|
|
1332
|
-
|
|
1333
|
-
```javascript
|
|
1334
|
-
const { data, error } = await supabase.functions.invoke('hello-world')
|
|
1335
|
-
|
|
1336
|
-
if (error) {
|
|
1337
|
-
console.error('Error invoking function:', error.message)
|
|
1338
|
-
} else {
|
|
1339
|
-
console.log('Function response:', data)
|
|
1340
|
-
}
|
|
1341
|
-
```
|
|
1342
|
-
|
|
1343
|
-
### Invoke Function with Body
|
|
1344
|
-
|
|
1345
|
-
```javascript
|
|
1346
|
-
const { data, error } = await supabase.functions.invoke('hello-world', {
|
|
1347
|
-
body: {
|
|
1348
|
-
name: 'JavaScript',
|
|
1349
|
-
message: 'Hello from client'
|
|
1350
|
-
}
|
|
1351
|
-
})
|
|
1352
|
-
|
|
1353
|
-
console.log('Response:', data)
|
|
1354
|
-
```
|
|
1355
|
-
|
|
1356
|
-
### Invoke Function with Custom Headers
|
|
1357
|
-
|
|
1358
|
-
```javascript
|
|
1359
|
-
const { data, error } = await supabase.functions.invoke('hello-world', {
|
|
1360
|
-
headers: {
|
|
1361
|
-
'X-Custom-Header': 'my-value'
|
|
1362
|
-
},
|
|
1363
|
-
body: { name: 'JavaScript' }
|
|
1364
|
-
})
|
|
1365
|
-
```
|
|
1366
|
-
|
|
1367
|
-
### Invoke Function with Method
|
|
1368
|
-
|
|
1369
|
-
```javascript
|
|
1370
|
-
const { data, error } = await supabase.functions.invoke('hello-world', {
|
|
1371
|
-
method: 'POST',
|
|
1372
|
-
body: { data: 'test' }
|
|
1373
|
-
})
|
|
1374
|
-
```
|
|
1375
|
-
|
|
1376
|
-
### Invoke Function on Specific Region
|
|
1377
|
-
|
|
1378
|
-
```javascript
|
|
1379
|
-
const { data, error } = await supabase.functions.invoke('hello-world', {
|
|
1380
|
-
region: 'us-west-1'
|
|
1381
|
-
})
|
|
1382
|
-
```
|
|
1383
|
-
|
|
1384
|
-
## Error Handling
|
|
1385
|
-
|
|
1386
|
-
All Supabase operations return an object with `data` and `error` properties.
|
|
1387
|
-
|
|
1388
|
-
### Basic Error Handling
|
|
1389
|
-
|
|
1390
|
-
```javascript
|
|
1391
|
-
const { data, error } = await supabase
|
|
1392
|
-
.from('countries')
|
|
1393
|
-
.select('*')
|
|
1394
|
-
|
|
1395
|
-
if (error) {
|
|
1396
|
-
console.error('Database error:', error.message)
|
|
1397
|
-
console.error('Error details:', error.details)
|
|
1398
|
-
console.error('Error hint:', error.hint)
|
|
1399
|
-
} else {
|
|
1400
|
-
console.log('Data:', data)
|
|
1401
|
-
}
|
|
1402
|
-
```
|
|
1403
|
-
|
|
1404
|
-
### Error Properties
|
|
1405
|
-
|
|
1406
|
-
```javascript
|
|
1407
|
-
if (error) {
|
|
1408
|
-
console.log('Message:', error.message)
|
|
1409
|
-
console.log('Details:', error.details)
|
|
1410
|
-
console.log('Hint:', error.hint)
|
|
1411
|
-
console.log('Code:', error.code)
|
|
1412
|
-
}
|
|
1413
|
-
```
|
|
1414
|
-
|
|
1415
|
-
### Try-Catch with Async/Await
|
|
1416
|
-
|
|
1417
|
-
```javascript
|
|
1418
|
-
try {
|
|
1419
|
-
const { data, error } = await supabase
|
|
1420
|
-
.from('countries')
|
|
1421
|
-
.select('*')
|
|
1422
|
-
|
|
1423
|
-
if (error) throw error
|
|
1424
|
-
|
|
1425
|
-
console.log('Data:', data)
|
|
1426
|
-
} catch (error) {
|
|
1427
|
-
console.error('Error:', error.message)
|
|
1428
|
-
}
|
|
1429
|
-
```
|
|
1430
|
-
|
|
1431
|
-
### Auth Error Handling
|
|
1432
|
-
|
|
1433
|
-
```javascript
|
|
1434
|
-
const { data, error } = await supabase.auth.signInWithPassword({
|
|
1435
|
-
email: 'user@example.com',
|
|
1436
|
-
password: 'wrong-password'
|
|
1437
|
-
})
|
|
1438
|
-
|
|
1439
|
-
if (error) {
|
|
1440
|
-
if (error.message.includes('Invalid login credentials')) {
|
|
1441
|
-
console.log('Wrong email or password')
|
|
1442
|
-
} else if (error.message.includes('Email not confirmed')) {
|
|
1443
|
-
console.log('Please verify your email')
|
|
1444
|
-
} else {
|
|
1445
|
-
console.error('Auth error:', error.message)
|
|
1446
|
-
}
|
|
1447
|
-
}
|
|
1448
|
-
```
|
|
1449
|
-
|
|
1450
|
-
### Storage Error Handling
|
|
1451
|
-
|
|
1452
|
-
```javascript
|
|
1453
|
-
const { data, error } = await supabase
|
|
1454
|
-
.storage
|
|
1455
|
-
.from('avatars')
|
|
1456
|
-
.upload('file.png', file)
|
|
1457
|
-
|
|
1458
|
-
if (error) {
|
|
1459
|
-
if (error.message.includes('Duplicate')) {
|
|
1460
|
-
console.log('File already exists')
|
|
1461
|
-
} else if (error.message.includes('Size')) {
|
|
1462
|
-
console.log('File too large')
|
|
1463
|
-
} else {
|
|
1464
|
-
console.error('Storage error:', error.message)
|
|
1465
|
-
}
|
|
1466
|
-
}
|
|
1467
|
-
```
|
|
1468
|
-
|
|
1469
|
-
## TypeScript Support
|
|
1470
|
-
|
|
1471
|
-
The Supabase SDK has full TypeScript support with automatic type inference.
|
|
1472
|
-
|
|
1473
|
-
### Define Database Types
|
|
1474
|
-
|
|
1475
|
-
```typescript
|
|
1476
|
-
import { createClient } from '@supabase/supabase-js'
|
|
1477
|
-
|
|
1478
|
-
interface Database {
|
|
1479
|
-
public: {
|
|
1480
|
-
Tables: {
|
|
1481
|
-
countries: {
|
|
1482
|
-
Row: {
|
|
1483
|
-
id: number
|
|
1484
|
-
name: string
|
|
1485
|
-
capital: string | null
|
|
1486
|
-
population: number | null
|
|
1487
|
-
}
|
|
1488
|
-
Insert: {
|
|
1489
|
-
id?: number
|
|
1490
|
-
name: string
|
|
1491
|
-
capital?: string | null
|
|
1492
|
-
population?: number | null
|
|
1493
|
-
}
|
|
1494
|
-
Update: {
|
|
1495
|
-
id?: number
|
|
1496
|
-
name?: string
|
|
1497
|
-
capital?: string | null
|
|
1498
|
-
population?: number | null
|
|
1499
|
-
}
|
|
1500
|
-
}
|
|
1501
|
-
}
|
|
1502
|
-
}
|
|
1503
|
-
}
|
|
1504
|
-
|
|
1505
|
-
const supabase = createClient<Database>(supabaseUrl, supabaseAnonKey)
|
|
1506
|
-
```
|
|
1507
|
-
|
|
1508
|
-
### Type-Safe Queries
|
|
1509
|
-
|
|
1510
|
-
```typescript
|
|
1511
|
-
const { data, error } = await supabase
|
|
1512
|
-
.from('countries')
|
|
1513
|
-
.select('*')
|
|
1514
|
-
|
|
1515
|
-
// data is automatically typed as Database['public']['Tables']['countries']['Row'][]
|
|
1516
|
-
```
|
|
1517
|
-
|
|
1518
|
-
### Generate Types from Database
|
|
1519
|
-
|
|
1520
|
-
Use the Supabase CLI to generate types:
|
|
1521
|
-
|
|
1522
|
-
```bash
|
|
1523
|
-
supabase gen types typescript --project-id your-project-id > types/supabase.ts
|
|
1524
|
-
```
|
|
1525
|
-
|
|
1526
|
-
Then import and use:
|
|
1527
|
-
|
|
1528
|
-
```typescript
|
|
1529
|
-
import { Database } from './types/supabase'
|
|
1530
|
-
|
|
1531
|
-
const supabase = createClient<Database>(supabaseUrl, supabaseAnonKey)
|
|
1532
|
-
```
|
|
1533
|
-
|
|
1534
|
-
## Advanced Configuration
|
|
1535
|
-
|
|
1536
|
-
### Custom Fetch Implementation
|
|
1537
|
-
|
|
1538
|
-
```javascript
|
|
1539
|
-
import { createClient } from '@supabase/supabase-js'
|
|
1540
|
-
|
|
1541
|
-
const supabase = createClient(supabaseUrl, supabaseAnonKey, {
|
|
1542
|
-
global: {
|
|
1543
|
-
fetch: customFetch
|
|
1544
|
-
}
|
|
1545
|
-
})
|
|
1546
|
-
```
|
|
1547
|
-
|
|
1548
|
-
### Custom Headers
|
|
1549
|
-
|
|
1550
|
-
```javascript
|
|
1551
|
-
const supabase = createClient(supabaseUrl, supabaseAnonKey, {
|
|
1552
|
-
global: {
|
|
1553
|
-
headers: {
|
|
1554
|
-
'x-application-name': 'my-app'
|
|
1555
|
-
}
|
|
1556
|
-
}
|
|
1557
|
-
})
|
|
1558
|
-
```
|
|
1559
|
-
|
|
1560
|
-
### Schema Configuration
|
|
1561
|
-
|
|
1562
|
-
```javascript
|
|
1563
|
-
const supabase = createClient(supabaseUrl, supabaseAnonKey, {
|
|
1564
|
-
db: {
|
|
1565
|
-
schema: 'private'
|
|
1566
|
-
}
|
|
1567
|
-
})
|
|
1568
|
-
```
|
|
1569
|
-
|
|
1570
|
-
### Auth Configuration
|
|
1571
|
-
|
|
1572
|
-
```javascript
|
|
1573
|
-
const supabase = createClient(supabaseUrl, supabaseAnonKey, {
|
|
1574
|
-
auth: {
|
|
1575
|
-
autoRefreshToken: true,
|
|
1576
|
-
persistSession: true,
|
|
1577
|
-
detectSessionInUrl: true,
|
|
1578
|
-
storage: customStorageAdapter,
|
|
1579
|
-
storageKey: 'my-app-auth-token',
|
|
1580
|
-
flowType: 'pkce'
|
|
1581
|
-
}
|
|
1582
|
-
})
|
|
1583
|
-
```
|
|
1584
|
-
|
|
1585
|
-
### Realtime Configuration
|
|
1586
|
-
|
|
1587
|
-
```javascript
|
|
1588
|
-
const supabase = createClient(supabaseUrl, supabaseAnonKey, {
|
|
1589
|
-
realtime: {
|
|
1590
|
-
params: {
|
|
1591
|
-
eventsPerSecond: 10
|
|
1592
|
-
}
|
|
1593
|
-
}
|
|
1594
|
-
})
|
|
1595
|
-
```
|
|
1596
|
-
|
|
1597
|
-
## Useful Links
|
|
1598
|
-
|
|
1599
|
-
- Documentation: https://supabase.com/docs
|
|
1600
|
-
- JavaScript SDK Reference: https://supabase.com/docs/reference/javascript/introduction
|
|
1601
|
-
- API Reference: https://supabase.com/docs/guides/api
|
|
1602
|
-
- Auth Documentation: https://supabase.com/docs/guides/auth
|
|
1603
|
-
- Database Documentation: https://supabase.com/docs/guides/database
|
|
1604
|
-
- Storage Documentation: https://supabase.com/docs/guides/storage
|
|
1605
|
-
- Realtime Documentation: https://supabase.com/docs/guides/realtime
|
|
1606
|
-
- Edge Functions: https://supabase.com/docs/guides/functions
|