# Supabase JavaScript SDK
The Supabase JavaScript SDK is a comprehensive isomorphic client library for interacting with Supabase services. It provides a unified interface to Supabase's Backend-as-a-Service platform, enabling developers to build applications with authentication, real-time PostgreSQL database operations, file storage, vector embeddings, and serverless edge functions. The SDK works across Node.js (v20+), Deno, modern browsers, React Native, Bun, and Cloudflare Workers, making it suitable for both client-side and server-side applications.
The monorepo contains six core packages: `@supabase/supabase-js` (the main unified SDK), `@supabase/auth-js` (authentication), `@supabase/postgrest-js` (database queries via PostgREST), `@supabase/realtime-js` (WebSocket-based real-time subscriptions), `@supabase/storage-js` (file and vector storage), and `@supabase/functions-js` (edge function invocation). Each package can be used independently or through the main supabase-js client which combines all functionality into a single cohesive API.
## Creating a Supabase Client
Initialize the main Supabase client to access all services including auth, database, storage, realtime, and functions through a single entry point.
```typescript
import { createClient } from '@supabase/supabase-js'
// Basic initialization
const supabase = createClient(
'https://xyzcompany.supabase.co',
'public-anon-key'
)
// With full configuration options
const supabase = createClient(
'https://xyzcompany.supabase.co',
'public-anon-key',
{
db: {
schema: 'public',
timeout: 30000, // 30 second timeout for database operations
},
auth: {
autoRefreshToken: true,
persistSession: true,
detectSessionInUrl: true,
storage: localStorage, // or AsyncStorage for React Native
storageKey: 'supabase-auth',
},
realtime: {
params: {
eventsPerSecond: 10,
},
},
global: {
headers: { 'x-custom-header': 'my-app' },
fetch: customFetch, // Custom fetch implementation for Cloudflare Workers
},
}
)
// Access individual services
const { data: user } = await supabase.auth.getUser()
const { data: rows } = await supabase.from('users').select('*')
const { data: files } = await supabase.storage.from('avatars').list()
const { data: result } = await supabase.functions.invoke('hello')
```
## Authentication - Sign Up
Create new user accounts with email/password, phone, or third-party OAuth providers. Supports email confirmation, phone OTP verification, and metadata storage.
```typescript
// Email/password signup
const { data, error } = await supabase.auth.signUp({
email: 'user@example.com',
password: 'securepassword123',
options: {
data: {
first_name: 'John',
last_name: 'Doe',
avatar_url: 'https://example.com/avatar.png',
},
emailRedirectTo: 'https://myapp.com/welcome',
},
})
if (error) {
console.error('Signup error:', error.message)
} else {
console.log('User created:', data.user?.id)
console.log('Session:', data.session?.access_token)
// Note: User may need to confirm email before session is active
}
// Phone signup with OTP
const { data, error } = await supabase.auth.signUp({
phone: '+1234567890',
password: 'securepassword123',
})
// Verify phone OTP
const { data: verifyData, error: verifyError } = await supabase.auth.verifyOtp({
phone: '+1234567890',
token: '123456',
type: 'sms',
})
```
## Authentication - Sign In
Authenticate users with various methods including email/password, magic links, OAuth providers, and single sign-on (SSO).
```typescript
// Email/password sign in
const { data, error } = await supabase.auth.signInWithPassword({
email: 'user@example.com',
password: 'securepassword123',
})
if (data.session) {
console.log('Logged in! Access token:', data.session.access_token)
console.log('User:', data.user?.email)
}
// Magic link (passwordless) sign in
const { error } = await supabase.auth.signInWithOtp({
email: 'user@example.com',
options: {
emailRedirectTo: 'https://myapp.com/dashboard',
},
})
// OAuth sign in (Google, GitHub, etc.)
const { data, error } = await supabase.auth.signInWithOAuth({
provider: 'google',
options: {
redirectTo: 'https://myapp.com/auth/callback',
scopes: 'email profile',
queryParams: {
access_type: 'offline',
prompt: 'consent',
},
},
})
// SSO sign in with SAML
const { data, error } = await supabase.auth.signInWithSSO({
domain: 'company.com',
options: {
redirectTo: 'https://myapp.com/dashboard',
},
})
// Anonymous sign in
const { data, error } = await supabase.auth.signInAnonymously({
options: {
data: { guest_preference: 'dark_mode' },
},
})
```
## Authentication - Session Management
Manage user sessions including retrieval, refresh, and sign out operations with proper security handling.
```typescript
// Get current session
const { data: { session }, error } = await supabase.auth.getSession()
if (session) {
console.log('Access token:', session.access_token)
console.log('Refresh token:', session.refresh_token)
console.log('Expires at:', new Date(session.expires_at! * 1000))
}
// Get current user (validates JWT with server)
const { data: { user }, error } = await supabase.auth.getUser()
if (user) {
console.log('User ID:', user.id)
console.log('Email:', user.email)
console.log('Metadata:', user.user_metadata)
}
// Listen to auth state changes
const { data: { subscription } } = supabase.auth.onAuthStateChange(
(event, session) => {
console.log('Auth event:', event) // SIGNED_IN, SIGNED_OUT, TOKEN_REFRESHED, etc.
if (event === 'SIGNED_IN') {
console.log('User signed in:', session?.user.email)
} else if (event === 'SIGNED_OUT') {
console.log('User signed out')
} else if (event === 'TOKEN_REFRESHED') {
console.log('Token refreshed')
}
}
)
// Update user attributes
const { data, error } = await supabase.auth.updateUser({
email: 'newemail@example.com',
password: 'newpassword123',
data: { display_name: 'John Updated' },
})
// Sign out
const { error } = await supabase.auth.signOut()
// Sign out from all devices
const { error } = await supabase.auth.signOut({ scope: 'global' })
// Cleanup subscription when done
subscription.unsubscribe()
```
## Authentication - Multi-Factor Authentication (MFA)
Enable two-factor authentication using TOTP (authenticator apps), phone SMS, or WebAuthn for enhanced security.
```typescript
// Enroll TOTP factor
const { data, error } = await supabase.auth.mfa.enroll({
factorType: 'totp',
friendlyName: 'My Authenticator App',
})
if (data) {
console.log('Scan QR code:', data.totp.qr_code) // Display to user
console.log('Or enter secret:', data.totp.secret)
console.log('Factor ID:', data.id)
}
// Challenge and verify MFA
const { data: challengeData, error: challengeError } = await supabase.auth.mfa.challenge({
factorId: 'factor-uuid-here',
})
const { data: verifyData, error: verifyError } = await supabase.auth.mfa.verify({
factorId: 'factor-uuid-here',
challengeId: challengeData.id,
code: '123456', // From authenticator app
})
// Combined challenge and verify
const { data, error } = await supabase.auth.mfa.challengeAndVerify({
factorId: 'factor-uuid-here',
code: '123456',
})
// List enrolled factors
const { data: factors, error } = await supabase.auth.mfa.listFactors()
console.log('TOTP factors:', factors?.totp)
console.log('Phone factors:', factors?.phone)
// Get current assurance level
const { data: aal, error } = await supabase.auth.mfa.getAuthenticatorAssuranceLevel()
console.log('Current level:', aal?.currentLevel) // 'aal1' or 'aal2'
console.log('Next level:', aal?.nextLevel)
// Unenroll a factor
const { error } = await supabase.auth.mfa.unenroll({ factorId: 'factor-uuid-here' })
```
## Database - Select Queries
Query data from PostgreSQL tables with filtering, sorting, pagination, and relationship loading through PostgREST.
```typescript
// Basic select all columns
const { data, error } = await supabase
.from('users')
.select('*')
// Select specific columns
const { data, error } = await supabase
.from('users')
.select('id, email, created_at')
// Select with relationships (foreign key joins)
const { data, error } = await supabase
.from('posts')
.select(`
id,
title,
content,
author:users(id, name, email),
comments(id, body, created_at)
`)
// Filtering with operators
const { data, error } = await supabase
.from('products')
.select('*')
.eq('category', 'electronics') // equals
.neq('status', 'discontinued') // not equals
.gt('price', 100) // greater than
.lte('stock', 50) // less than or equal
.like('name', '%phone%') // pattern matching
.ilike('description', '%SALE%') // case-insensitive pattern
.in('brand', ['Apple', 'Samsung']) // in array
.is('deleted_at', null) // is null
.contains('tags', ['featured']) // array contains
.range('price', 10, 100) // between range
// Ordering and pagination
const { data, error, count } = await supabase
.from('posts')
.select('*', { count: 'exact' })
.order('created_at', { ascending: false })
.range(0, 9) // First 10 records (0-indexed)
console.log('Total count:', count)
console.log('Page data:', data)
// Get single record
const { data, error } = await supabase
.from('users')
.select('*')
.eq('id', 'user-uuid')
.single()
// Maybe single (returns null if not found, no error)
const { data, error } = await supabase
.from('users')
.select('*')
.eq('email', 'maybe@exists.com')
.maybeSingle()
```
## Database - Insert, Update, Delete
Perform data mutations with conflict handling, returning updated data, and batch operations.
```typescript
// Insert single record
const { data, error } = await supabase
.from('users')
.insert({
email: 'new@example.com',
name: 'New User',
metadata: { signup_source: 'web' },
})
.select()
.single()
console.log('Created user:', data)
// Insert multiple records
const { data, error } = await supabase
.from('products')
.insert([
{ name: 'Product A', price: 29.99, category: 'electronics' },
{ name: 'Product B', price: 49.99, category: 'electronics' },
{ name: 'Product C', price: 19.99, category: 'accessories' },
])
.select()
// Upsert (insert or update on conflict)
const { data, error } = await supabase
.from('settings')
.upsert(
{ user_id: 'user-uuid', theme: 'dark', language: 'en' },
{ onConflict: 'user_id' }
)
.select()
.single()
// Update records
const { data, error } = await supabase
.from('posts')
.update({
title: 'Updated Title',
updated_at: new Date().toISOString(),
})
.eq('id', 'post-uuid')
.select()
.single()
// Update multiple records
const { data, error } = await supabase
.from('products')
.update({ on_sale: true, discount: 0.2 })
.eq('category', 'clearance')
.select()
// Delete records
const { error } = await supabase
.from('comments')
.delete()
.eq('id', 'comment-uuid')
// Delete with returning deleted data
const { data: deleted, error } = await supabase
.from('temp_records')
.delete()
.lt('created_at', '2024-01-01')
.select()
console.log('Deleted records:', deleted?.length)
```
## Database - RPC (Stored Procedures)
Call PostgreSQL functions and stored procedures with arguments, filters, and various return types.
```typescript
// Call function without arguments
const { data, error } = await supabase.rpc('get_server_time')
console.log('Server time:', data)
// Call function with arguments
const { data, error } = await supabase.rpc('calculate_order_total', {
order_id: 'order-uuid',
include_tax: true,
})
console.log('Order total:', data)
// Call function returning table (set-returning function)
const { data, error } = await supabase
.rpc('search_products', { search_term: 'laptop' })
.eq('in_stock', true)
.order('relevance', { ascending: false })
.limit(10)
// Bulk processing with array arguments
const { data, error } = await supabase.rpc('process_batch', {
ids: ['id1', 'id2', 'id3'],
operation: 'archive',
})
// Read-only function call (uses GET instead of POST)
const { data, error } = await supabase.rpc(
'get_dashboard_stats',
{ user_id: 'user-uuid' },
{ get: true }
)
// Get count only (HEAD request)
const { count, error } = await supabase
.rpc('list_active_users', {}, { head: true, count: 'exact' })
console.log('Active users count:', count)
```
## Realtime - Database Changes (Postgres CDC)
Subscribe to real-time database changes using Postgres Change Data Capture for INSERT, UPDATE, and DELETE events.
```typescript
// Subscribe to all changes in a table
const channel = supabase
.channel('db-changes')
.on(
'postgres_changes',
{ event: '*', schema: 'public', table: 'messages' },
(payload) => {
console.log('Change type:', payload.eventType)
console.log('New data:', payload.new)
console.log('Old data:', payload.old)
}
)
.subscribe((status) => {
if (status === 'SUBSCRIBED') {
console.log('Listening for database changes!')
}
})
// Subscribe to specific events with filters
const channel = supabase
.channel('user-posts')
.on(
'postgres_changes',
{
event: 'INSERT',
schema: 'public',
table: 'posts',
filter: 'user_id=eq.user-uuid',
},
(payload) => {
console.log('New post:', payload.new)
// Update UI with new post
}
)
.on(
'postgres_changes',
{
event: 'UPDATE',
schema: 'public',
table: 'posts',
filter: 'user_id=eq.user-uuid',
},
(payload) => {
console.log('Post updated:', payload.new)
}
)
.on(
'postgres_changes',
{ event: 'DELETE', schema: 'public', table: 'posts' },
(payload) => {
console.log('Post deleted, ID:', payload.old.id)
}
)
.subscribe()
// Cleanup when done
await supabase.removeChannel(channel)
```
## Realtime - Broadcast
Send ephemeral messages between clients in real-time for features like cursor sharing, typing indicators, and live notifications.
```typescript
// Create broadcast channel
const channel = supabase.channel('room-1', {
config: {
broadcast: {
ack: true, // Wait for server acknowledgment
self: false, // Don't receive own messages
},
},
})
// Listen for broadcast messages
channel.on('broadcast', { event: 'cursor-move' }, (payload) => {
console.log('User moved cursor:', payload.payload)
// { userId: 'user-1', x: 150, y: 200 }
})
channel.on('broadcast', { event: 'typing' }, (payload) => {
console.log('User is typing:', payload.payload.userId)
})
// Subscribe and send messages
channel.subscribe(async (status) => {
if (status === 'SUBSCRIBED') {
// Send cursor position
const ack = await channel.send({
type: 'broadcast',
event: 'cursor-move',
payload: { userId: 'current-user', x: 150, y: 200 },
})
console.log('Message acknowledged:', ack)
// Send typing indicator
await channel.send({
type: 'broadcast',
event: 'typing',
payload: { userId: 'current-user', typing: true },
})
}
})
// Broadcast replay for private channels (get historical messages)
const replayChannel = supabase.channel('private-room', {
config: {
private: true,
broadcast: {
replay: {
since: Date.now() - 12 * 60 * 60 * 1000, // Last 12 hours
limit: 25,
},
},
},
})
replayChannel
.on('broadcast', { event: 'message' }, (payload) => {
if (payload.meta?.replayed) {
console.log('Historical message:', payload.payload)
} else {
console.log('New message:', payload.payload)
}
})
.subscribe()
```
## Realtime - Presence
Track and synchronize online user state across clients with automatic join/leave detection and custom presence data.
```typescript
// Create presence channel
const channel = supabase.channel('online-users', {
config: {
presence: {
key: 'user-uuid', // Unique key for this client
},
},
})
// Handle presence sync
channel.on('presence', { event: 'sync' }, () => {
const state = channel.presenceState()
console.log('All online users:', state)
// { 'user-1': [{ status: 'online', ... }], 'user-2': [{ status: 'away', ... }] }
const onlineUsers = Object.keys(state).length
console.log('Online count:', onlineUsers)
})
// Handle user join
channel.on('presence', { event: 'join' }, ({ key, newPresences }) => {
console.log('User joined:', key)
console.log('Their state:', newPresences)
})
// Handle user leave
channel.on('presence', { event: 'leave' }, ({ key, leftPresences }) => {
console.log('User left:', key)
})
// Subscribe and track presence
channel.subscribe(async (status) => {
if (status === 'SUBSCRIBED') {
// Track this user's presence
const trackStatus = await channel.track({
userId: 'current-user',
status: 'online',
lastSeen: new Date().toISOString(),
currentPage: '/dashboard',
})
console.log('Track status:', trackStatus)
}
})
// Update presence state
await channel.track({
userId: 'current-user',
status: 'away',
lastSeen: new Date().toISOString(),
})
// Remove presence (go offline)
await channel.untrack()
// Cleanup
await supabase.removeChannel(channel)
```
## Storage - File Operations
Upload, download, list, move, and delete files with support for public/private buckets and signed URLs.
```typescript
// Upload file
const file = document.querySelector('input[type="file"]').files[0]
const { data, error } = await supabase.storage
.from('avatars')
.upload(`users/${userId}/avatar.png`, file, {
cacheControl: '3600',
contentType: 'image/png',
upsert: true, // Overwrite if exists
})
console.log('Uploaded to:', data?.path)
// Upload from Blob/ArrayBuffer
const imageBlob = new Blob([arrayBuffer], { type: 'image/jpeg' })
const { data, error } = await supabase.storage
.from('images')
.upload('photos/sunset.jpg', imageBlob)
// Download file
const { data, error } = await supabase.storage
.from('documents')
.download('reports/annual-2024.pdf')
if (data) {
const url = URL.createObjectURL(data)
// Use url for display or download
}
// Get public URL (for public buckets)
const { data } = supabase.storage
.from('public-assets')
.getPublicUrl('images/logo.png', {
transform: {
width: 200,
height: 200,
resize: 'cover',
},
})
console.log('Public URL:', data.publicUrl)
// Create signed URL (for private buckets)
const { data, error } = await supabase.storage
.from('private-docs')
.createSignedUrl('contracts/agreement.pdf', 3600) // 1 hour expiry
console.log('Signed URL:', data?.signedUrl)
// List files in a folder
const { data, error } = await supabase.storage
.from('uploads')
.list('users/123', {
limit: 100,
offset: 0,
sortBy: { column: 'created_at', order: 'desc' },
})
// Move file
const { error } = await supabase.storage
.from('uploads')
.move('temp/file.pdf', 'permanent/file.pdf')
// Delete files
const { error } = await supabase.storage
.from('uploads')
.remove(['temp/file1.pdf', 'temp/file2.pdf'])
```
## Storage - Bucket Management
Create, configure, and manage storage buckets with public/private access control and pagination.
```typescript
// Create a new bucket
const { data, error } = await supabase.storage.createBucket('documents', {
public: false,
fileSizeLimit: 10485760, // 10MB
allowedMimeTypes: ['application/pdf', 'image/*'],
})
// Get bucket details
const { data, error } = await supabase.storage.getBucket('documents')
console.log('Bucket:', data?.name)
console.log('Public:', data?.public)
// List all buckets with pagination
const { data, error } = await supabase.storage.listBuckets({
limit: 10,
offset: 0,
sortColumn: 'created_at',
sortOrder: 'desc',
search: 'prod',
})
// Update bucket settings
const { data, error } = await supabase.storage.updateBucket('documents', {
public: true,
fileSizeLimit: 52428800, // 50MB
})
// Empty a bucket (delete all files)
const { error } = await supabase.storage.emptyBucket('temp-uploads')
// Delete a bucket (must be empty)
const { error } = await supabase.storage.deleteBucket('old-bucket')
```
## Storage - Vector Embeddings
Store and query high-dimensional vector embeddings for semantic search, AI recommendations, and similarity matching.
```typescript
// Create vector bucket
const { data, error } = await supabase.storage.vectors.createBucket('embeddings')
// Create vector index
const bucket = supabase.storage.vectors.from('embeddings')
const { error } = await bucket.createIndex({
indexName: 'documents',
dataType: 'float32',
dimension: 1536, // OpenAI embedding dimension
distanceMetric: 'cosine', // 'cosine' | 'euclidean' | 'dotproduct'
metadataConfiguration: {
nonFilterableMetadataKeys: ['raw_text'], // Exclude from filtering
},
})
// Insert vectors
const index = bucket.index('documents')
const { error } = await index.putVectors({
vectors: [
{
key: 'doc-1',
data: { float32: embedding1 }, // 1536-dimensional array
metadata: { title: 'Introduction', category: 'docs', page: 1 },
},
{
key: 'doc-2',
data: { float32: embedding2 },
metadata: { title: 'Getting Started', category: 'docs', page: 2 },
},
],
})
// Query similar vectors (semantic search)
const { data, error } = await index.queryVectors({
queryVector: { float32: queryEmbedding },
topK: 10,
filter: { category: 'docs' },
returnDistance: true,
returnMetadata: true,
})
if (data) {
data.matches.forEach((match) => {
console.log(`${match.key}: distance=${match.distance}`)
console.log('Metadata:', match.metadata)
})
}
// Get vectors by key
const { data, error } = await index.getVectors({
keys: ['doc-1', 'doc-2'],
returnData: true,
returnMetadata: true,
})
// List/scan all vectors
const { data, error } = await index.listVectors({
maxResults: 500,
returnMetadata: true,
})
// Delete vectors
await index.deleteVectors({ keys: ['doc-1', 'doc-2'] })
// Delete index and bucket
await bucket.deleteIndex('documents')
await supabase.storage.vectors.deleteBucket('embeddings')
```
## Edge Functions
Invoke Supabase Edge Functions (Deno-based serverless functions) with various request/response types and error handling.
```typescript
// Basic function invocation
const { data, error } = await supabase.functions.invoke('hello-world', {
body: { name: 'John' },
})
console.log('Response:', data) // { message: 'Hello John!' }
// With custom headers and response type
const { data, error } = await supabase.functions.invoke('generate-pdf', {
body: { template: 'invoice', orderId: '12345' },
headers: {
'x-custom-header': 'custom-value',
},
})
// Stream response
const { data, error } = await supabase.functions.invoke('stream-chat', {
body: { message: 'Tell me a story' },
})
if (data instanceof ReadableStream) {
const reader = data.getReader()
const decoder = new TextDecoder()
while (true) {
const { done, value } = await reader.read()
if (done) break
console.log('Chunk:', decoder.decode(value))
}
}
// Invoke with specific region
const { data, error } = await supabase.functions.invoke('process-data', {
body: { dataId: 'xyz' },
region: 'us-east-1',
})
// Handle different response types
const { data, error } = await supabase.functions.invoke('get-image', {
body: { imageId: '123' },
})
if (data instanceof Blob) {
const imageUrl = URL.createObjectURL(data)
// Use imageUrl in
element
}
// Error handling
const { data, error } = await supabase.functions.invoke('protected-function', {
body: { action: 'secret' },
})
if (error) {
if (error instanceof FunctionsHttpError) {
console.log('Function returned an error:', error.message)
} else if (error instanceof FunctionsRelayError) {
console.log('Relay error:', error.message)
} else if (error instanceof FunctionsFetchError) {
console.log('Network error:', error.message)
}
}
```
## TypeScript Support with Generated Types
Leverage TypeScript for full type safety by generating types from your database schema.
```typescript
// Generate types using Supabase CLI:
// npx supabase gen types typescript --project-id your-project-id > database.types.ts
import { createClient } from '@supabase/supabase-js'
import { Database } from './database.types'
// Create typed client
const supabase = createClient(
'https://xyzcompany.supabase.co',
'public-anon-key'
)
// Full type inference for queries
const { data: users } = await supabase
.from('users')
.select('id, email, profile:profiles(avatar_url, bio)')
.eq('status', 'active')
// TypeScript knows: users is { id: string, email: string, profile: { avatar_url: string, bio: string } }[]
// Type-safe inserts
const { data, error } = await supabase
.from('posts')
.insert({
title: 'My Post',
content: 'Content here',
user_id: 'user-uuid',
// TypeScript error if you add invalid fields or wrong types
})
.select()
.single()
// Type-safe RPC calls
const { data } = await supabase.rpc('search_posts', {
search_term: 'typescript',
limit_count: 10,
})
// Access different schemas
const { data } = await supabase
.schema('analytics')
.from('events')
.select('*')
// Override types when needed
const { data } = await supabase
.from('complex_view')
.select('*')
.overrideTypes<{ custom_field: string; computed: number }[]>()
```
## Server-Side Usage and Admin Operations
Use the SDK in server-side contexts with service role keys for admin operations that bypass Row Level Security.
```typescript
import { createClient } from '@supabase/supabase-js'
// Server-side client with service role (bypasses RLS)
const supabaseAdmin = createClient(
'https://xyzcompany.supabase.co',
'service-role-secret-key',
{
auth: {
autoRefreshToken: false,
persistSession: false,
},
}
)
// Admin auth operations
const { data, error } = await supabaseAdmin.auth.admin.listUsers({
page: 1,
perPage: 100,
})
// Create user as admin
const { data: newUser, error } = await supabaseAdmin.auth.admin.createUser({
email: 'user@example.com',
password: 'securepassword',
email_confirm: true,
user_metadata: { role: 'customer' },
})
// Update user as admin
const { data, error } = await supabaseAdmin.auth.admin.updateUserById(
'user-uuid',
{
email: 'newemail@example.com',
user_metadata: { verified: true },
ban_duration: 'none', // Unban user
}
)
// Delete user
const { error } = await supabaseAdmin.auth.admin.deleteUser('user-uuid')
// Database operations bypassing RLS
const { data, error } = await supabaseAdmin
.from('internal_logs')
.insert({ action: 'system_backup', timestamp: new Date() })
// Custom access token for server components
const supabaseWithToken = createClient(
'https://xyzcompany.supabase.co',
'public-anon-key',
{
global: {
headers: {
Authorization: `Bearer ${customJwt}`,
},
},
}
)
```
The Supabase JavaScript SDK provides a complete toolkit for building modern applications with real-time database subscriptions, secure authentication flows, file storage with CDN delivery, vector search capabilities, and serverless function execution. The unified API design makes it straightforward to combine these features - for example, authenticating users, storing their files, listening for real-time updates on their data, and invoking edge functions all through a single client instance. Common integration patterns include using auth state changes to update realtime channel subscriptions, combining storage uploads with database metadata records, and using vector embeddings with PostgreSQL full-text search for hybrid search implementations.
For production deployments, the SDK supports Row Level Security (RLS) policies for fine-grained access control, automatic token refresh for long-running sessions, and optimistic UI updates with realtime confirmations. The TypeScript-first design with generated database types ensures type safety across the entire data layer, catching errors at compile time rather than runtime. Whether building a real-time collaborative app, a content management system, or an AI-powered search application, the Supabase JS SDK provides the foundational building blocks with consistent patterns and comprehensive error handling.