
Supabase Js
- 37 installs
- 946 repo stars
- Updated August 2, 2026
- fcakyon/claude-codex-settings
Helps with ai & agent building tasks.
About
supabase-js is a Claude Code skill for ai & agent building. It helps solo builders move faster with AI-assisted coding.
- supabase-js
- AI & Agent Building
- AI-coding skill
Supabase Js by the numbers
- 37 all-time installs (skills.sh)
- Ranked #8,484 of 16,556 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 3, 2026 (Skillselion catalog sync)
npx skills add https://github.com/fcakyon/claude-codex-settings --skill supabase-jsAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 37 |
|---|---|
| repo stars | ★ 946 |
| Last updated | August 2, 2026 |
| Repository | fcakyon/claude-codex-settings ↗ |
What it does
Helps with ai & agent building tasks.
Files
Supabase JavaScript SDK Skill
Skill for building applications with the @supabase/supabase-js SDK. Covers Auth, Database (PostgREST), Storage, Realtime, and Edge Functions.
The SDK docs at https://supabase.com/docs/reference/javascript are the source of truth. The reference files alongside this skill contain source code and READMEs extracted from the monorepo for quick lookup.
Setup
npm install @supabase/supabase-jsimport { createClient } from '@supabase/supabase-js'
const supabase = createClient('https://xyzcompany.supabase.co', 'public-anon-key')For type-safe queries, generate types from your database schema:
supabase gen types typescript --project-id your-project-id > database.types.tsimport { createClient } from '@supabase/supabase-js'
import type { Database } from './database.types'
const supabase = createClient<Database>(SUPABASE_URL, SUPABASE_ANON_KEY)Quick Decision Trees
"I need to query data"
Database query?
├─ Select rows → supabase.from('table').select('*')
├─ Filter rows → .select().eq('col', val) / .gt() / .lt() / .in() / .like()
├─ Join tables → .select('*, other_table(*)') or .select('*, other_table!fk(*)')
├─ Insert → supabase.from('table').insert({ col: val })
├─ Upsert → supabase.from('table').upsert({ id: 1, col: val })
├─ Update → supabase.from('table').update({ col: val }).eq('id', 1)
├─ Delete → supabase.from('table').delete().eq('id', 1)
├─ Call RPC function → supabase.rpc('function_name', { arg: val })
├─ Count rows → .select('*', { count: 'exact', head: true })
├─ Pagination → .range(0, 9) or .limit(10).offset(20)
└─ Order → .order('created_at', { ascending: false })"I need authentication"
Auth?
├─ Email/password sign up → supabase.auth.signUp({ email, password })
├─ Email/password sign in → supabase.auth.signInWithPassword({ email, password })
├─ OAuth (Google, GitHub, etc.) → supabase.auth.signInWithOAuth({ provider: 'google' })
├─ Magic link → supabase.auth.signInWithOtp({ email })
├─ Phone OTP → supabase.auth.signInWithOtp({ phone })
├─ Sign out → supabase.auth.signOut()
├─ Get current user → supabase.auth.getUser()
├─ Get session → supabase.auth.getSession()
├─ Listen to auth changes → supabase.auth.onAuthStateChange((event, session) => {})
├─ Reset password → supabase.auth.resetPasswordForEmail(email)
├─ Update user → supabase.auth.updateUser({ data: { name: 'New' } })
└─ Admin operations → supabase.auth.admin.listUsers() / .deleteUser(id)"I need file storage"
Storage?
├─ Upload file → supabase.storage.from('bucket').upload('path/file.png', file)
├─ Download file → supabase.storage.from('bucket').download('path/file.png')
├─ Get public URL → supabase.storage.from('bucket').getPublicUrl('path/file.png')
├─ Create signed URL → supabase.storage.from('bucket').createSignedUrl('path', 3600)
├─ List files → supabase.storage.from('bucket').list('folder')
├─ Move file → supabase.storage.from('bucket').move('old/path', 'new/path')
├─ Remove file → supabase.storage.from('bucket').remove(['path/file.png'])
├─ Create bucket → supabase.storage.createBucket('name', { public: false })
└─ List buckets → supabase.storage.listBuckets()"I need realtime"
Realtime?
├─ Listen to DB changes → supabase.channel('name')
│ .on('postgres_changes', { event: 'INSERT', schema: 'public', table: 'messages' }, handler)
│ .subscribe()
├─ Broadcast messages → channel.send({ type: 'broadcast', event: 'cursor', payload: { x, y } })
├─ Listen to broadcasts → .on('broadcast', { event: 'cursor' }, handler)
├─ Presence (who's online) → channel.track({ user_id, online_at })
│ .on('presence', { event: 'sync' }, () => channel.presenceState())
├─ Unsubscribe → supabase.removeChannel(channel)
└─ Unsubscribe all → supabase.removeAllChannels()"I need edge functions"
Edge Functions?
├─ Invoke function → supabase.functions.invoke('function-name', { body: { key: 'val' } })
├─ With custom headers → .invoke('fn', { headers: { 'x-custom': 'val' }, body })
└─ Set region → .invoke('fn', { body, region: 'us-east-1' })Common Patterns
Server-side with service role key
// Server-side only - bypasses RLS
const supabase = createClient(SUPABASE_URL, SUPABASE_SERVICE_ROLE_KEY, {
auth: { persistSession: false }
})React/Next.js auth
import { createClient } from '@supabase/supabase-js'
import { useEffect, useState } from 'react'
const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY)
function useUser() {
const [user, setUser] = useState(null)
useEffect(() => {
supabase.auth.getUser().then(({ data }) => setUser(data.user))
const { data: { subscription } } = supabase.auth.onAuthStateChange(
(_, session) => setUser(session?.user ?? null)
)
return () => subscription.unsubscribe()
}, [])
return user
}Typed database queries
// Generated types give autocomplete for table names, column names, and return types
const { data, error } = await supabase
.from('profiles') // autocompleted table name
.select('id, username') // autocompleted columns
.eq('id', userId) // type-safe filter
.single() // returns single row or error
// data is typed as { id: string; username: string } | nullPackage Index
| Package | Sub-client | Reference |
|---|---|---|
@supabase/supabase-js | createClient() | references/supabase-js/ |
@supabase/auth-js | .auth | references/auth-js/ |
@supabase/postgrest-js | .from(), .rpc() | references/postgrest-js/ |
@supabase/realtime-js | .channel(), .realtime | references/realtime-js/ |
@supabase/storage-js | .storage | references/storage-js/ |
@supabase/functions-js | .functions | references/functions-js/ |
<br /> <p align="center"> <a href="https://supabase.io"> <picture> <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/supabase/supabase/master/packages/common/assets/images/supabase-logo-wordmark--dark.svg"> <source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/supabase/supabase/master/packages/common/assets/images/supabase-logo-wordmark--light.svg"> <img alt="Supabase Logo" width="300" src="https://raw.githubusercontent.com/supabase/supabase/master/packages/common/assets/images/logo-preview.jpg"> </picture> </a>
<h1 align="center">Supabase Auth JS SDK</h1>
<h3 align="center">An isomorphic JavaScript SDK for the <a href="https://github.com/supabase/auth">Supabase Auth</a> API.</h3>
<p align="center"> <a href="https://supabase.com/docs/guides/auth">Guides</a> · <a href="https://supabase.com/docs/reference/javascript/auth-signup">Reference Docs</a> · <a href="https://supabase.github.io/supabase-js/auth-js/v2/spec.json">TypeDoc</a> </p> </p>
<div align="center">
   
</div>
Requirements
- Node.js 20 or later (Node.js 18 support dropped as of October 31, 2025)
- For browser support, all modern browsers are supported
⚠️ Node.js 18 Deprecation Notice
>
Node.js 18 reached end-of-life on April 30, 2025. As announced in our deprecation notice, support for Node.js 18 was dropped on October 31, 2025.
Quick start
Install
npm install --save @supabase/auth-jsUsage
import { AuthClient } from '@supabase/auth-js'
const GOTRUE_URL = 'http://localhost:9999'
const auth = new AuthClient({ url: GOTRUE_URL })signUp(): https://supabase.com/docs/reference/javascript/auth-signupsignIn(): https://supabase.com/docs/reference/javascript/auth-signinsignOut(): https://supabase.com/docs/reference/javascript/auth-signout
Custom fetch implementation
auth-js uses the `cross-fetch` library to make HTTP requests, but an alternative fetch implementation can be provided as an option. This is most useful in environments where cross-fetch is not compatible, for instance Cloudflare Workers:
import { AuthClient } from '@supabase/auth-js'
const AUTH_URL = 'http://localhost:9999'
const auth = new AuthClient({ url: AUTH_URL, fetch: fetch })Development
This package is part of the Supabase JavaScript monorepo. To work on this package:
Building
# Complete build (from monorepo root)
npx nx build auth-js
# Build with watch mode for development
npx nx build auth-js --watch
# Individual build targets
npx nx build:main auth-js # CommonJS build (dist/main/)
npx nx build:module auth-js # ES Modules build (dist/module/)
# Other useful commands
npx nx lint auth-js # Run ESLint
npx nx typecheck auth-js # TypeScript type checking
npx nx docs auth-js # Generate documentationBuild Outputs
- CommonJS (`dist/main/`) - For Node.js environments
- ES Modules (`dist/module/`) - For modern bundlers (Webpack, Vite, Rollup)
- TypeScript definitions (`dist/module/index.d.ts`) - Type definitions for TypeScript projects
Testing
The auth-js package has two test suites:
1. CLI Tests - Main test suite using Supabase CLI (331 tests) 2. Docker Tests - Edge case tests requiring specific GoTrue configurations (11 tests)
Prerequisites
- Supabase CLI - Required for main test suite (installation guide)
- Docker - Required for edge case tests
Running Tests
# Run main test suite with Supabase CLI (recommended)
npx nx test:auth auth-js
# Run Docker-only edge case tests
npx nx test:docker auth-js
# Run both test suites
npx nx test:auth auth-js && npx nx test:docker auth-jsMain Test Suite (Supabase CLI)
The test:auth command automatically:
1. Stops any existing Supabase instance 2. Starts a local Supabase instance via CLI 3. Runs the test suite (excludes docker-tests/ folder) 4. Cleans up after tests complete
# Individual commands for manual control
npx nx test:infra auth-js # Start Supabase CLI
npx nx test:suite auth-js # Run tests only
npx nx test:clean-post auth-js # Stop Supabase CLIDocker Tests (Edge Cases)
The test:docker target runs tests that require specific GoTrue configurations not possible with a single Supabase CLI instance:
- Signup disabled - Tests for disabled signup functionality
- Asymmetric JWT (RS256) - Tests for RS256 JWT verification
- Phone OTP / SMS - Tests requiring Twilio SMS provider
- Anonymous sign-in disabled - Tests for disabled anonymous auth
These tests are located in test/docker-tests/ and use the Docker Compose setup in infra/docker-compose.yml.
# Individual commands for manual control
npx nx test:docker:infra auth-js # Start Docker containers
npx nx test:docker:suite auth-js # Run Docker tests only
npx nx test:docker:clean-post auth-js # Stop Docker containersDevelopment Testing
For actively developing and debugging tests:
# Start Supabase CLI once
npx nx test:infra auth-js
# Run tests multiple times (faster since instance stays up)
npx nx test:suite auth-js
# Clean up when done
npx nx test:clean-post auth-jsTest Infrastructure
| Suite | Infrastructure | Configuration |
|---|---|---|
| CLI Tests | Supabase CLI | test/supabase/config.toml |
| Docker Tests | Docker Compose | infra/docker-compose.yml |
Contributing
We welcome contributions! Please see our Contributing Guide for details on how to get started.
For major changes or if you're unsure about something, please open an issue first to discuss your proposed changes.
<br /> <p align="center"> <a href="https://supabase.io"> <picture> <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/supabase/supabase/master/packages/common/assets/images/supabase-logo-wordmark--dark.svg"> <source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/supabase/supabase/master/packages/common/assets/images/supabase-logo-wordmark--light.svg"> <img alt="Supabase Logo" width="300" src="https://raw.githubusercontent.com/supabase/supabase/master/packages/common/assets/images/logo-preview.jpg"> </picture> </a>
<h1 align="center">Supabase Functions JS SDK</h1>
<h3 align="center">JavaScript SDK to interact with Supabase Edge Functions.</h3>
<p align="center"> <a href="https://supabase.com/docs/guides/functions">Guides</a> · <a href="https://supabase.com/docs/reference/javascript/functions-invoke">Reference Docs</a> · <a href="https://supabase.github.io/supabase-js/functions-js/v2/spec.json">TypeDoc</a> </p> </p>
<div align="center">
   
</div>
Requirements
- Node.js 20 or later (Node.js 18 support dropped as of October 31, 2025)
- For browser support, all modern browsers are supported
⚠️ Node.js 18 Deprecation Notice
>
Node.js 18 reached end-of-life on April 30, 2025. As announced in our deprecation notice, support for Node.js 18 was dropped on October 31, 2025.
Quick Start
Installation
npm install @supabase/functions-jsUsage
import { FunctionsClient } from '@supabase/functions-js'
const functionsUrl = 'https://<project_ref>.supabase.co/functions/v1'
const anonKey = '<anon_key>'
const functions = new FunctionsClient(functionsUrl, {
headers: {
Authorization: `Bearer ${anonKey}`,
},
})
// Invoke a function
const { data, error } = await functions.invoke('hello-world', {
body: { name: 'Functions' },
})Development
This package is part of the Supabase JavaScript monorepo. To work on this package:
Building
# Complete build (from monorepo root)
npx nx build functions-js
# Build with watch mode for development
npx nx build functions-js --watch
# Individual build targets
npx nx build:main functions-js # CommonJS build (dist/main/)
npx nx build:module functions-js # ES Modules build (dist/module/)
# Other useful commands
npx nx clean functions-js # Clean build artifacts
npx nx typecheck functions-js # TypeScript type checking
npx nx docs functions-js # Generate documentationBuild Outputs
- CommonJS (`dist/main/`) - For Node.js environments
- ES Modules (`dist/module/`) - For modern bundlers (Webpack, Vite, Rollup)
- TypeScript definitions (`dist/module/index.d.ts`) - Type definitions for TypeScript projects
Testing
Docker Required for relay tests. The functions-js tests use testcontainers to spin up a Deno relay server for testing Edge Function invocations.
# Run all tests (from monorepo root)
npx nx test functions-js
# Run tests with coverage report
npx nx test functions-js --coverage
# Run tests in watch mode during development
npx nx test functions-js --watch
# CI test command (runs with coverage)
npx nx test:ci functions-jsTest Requirements
- Node.js 20+ - Required for testcontainers
- Docker - Must be installed and running for relay tests
- No Supabase instance needed - Tests use mocked services and testcontainers
What Gets Tested
- Function invocation - Testing the
invoke()method with various options - Relay functionality - Using a containerized Deno relay to test real Edge Function scenarios
- Error handling - Ensuring proper error responses and retries
- Request/response models - Validating headers, body, and response formats
Contributing
We welcome contributions! Please see our Contributing Guide for details on how to get started.
For major changes or if you're unsure about something, please open an issue first to discuss your proposed changes.
<br /> <p align="center"> <a href="https://supabase.io"> <picture> <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/supabase/supabase/master/packages/common/assets/images/supabase-logo-wordmark--dark.svg"> <source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/supabase/supabase/master/packages/common/assets/images/supabase-logo-wordmark--light.svg"> <img alt="Supabase Logo" width="300" src="https://raw.githubusercontent.com/supabase/supabase/master/packages/common/assets/images/logo-preview.jpg"> </picture> </a>
<h1 align="center">Supabase JS SDK</h1>
<p align="center"> <a href="https://supabase.com/docs/guides/getting-started">Guides</a> · <a href="https://supabase.com/docs/reference/javascript/introduction">Reference Docs</a> </p> </p>
<div align="center">
   
</div>
For contributors: Repository Structure Changed
>
This repository has been restructured as a monorepo. All libraries, includingsupabase-jsitself, have moved topackages/core/:
>
| What You're Looking For | Where It Is Now |
| ----------------------- | ---------------------------- |
| Main supabase-js code | packages/core/supabase-js/ || Other libraries | packages/core/*/ |>
Read the [Migration Guide](./docs/MIGRATION.md) to learn more.
📦 Libraries
This monorepo contains the complete suite of Supabase JavaScript SDK:
| Library | Description |
|---|---|
| [@supabase/supabase-js](./packages/core/supabase-js) | Main isomorphic SDK for Supabase |
| [@supabase/auth-js](./packages/core/auth-js) | Authentication SDK |
| [@supabase/postgrest-js](./packages/core/postgrest-js) | PostgREST SDK for database operations |
| [@supabase/realtime-js](./packages/core/realtime-js) | Real-time subscriptions SDK |
| [@supabase/storage-js](./packages/core/storage-js) | File storage SDK |
| [@supabase/functions-js](./packages/core/functions-js) | Edge Functions SDK |
Support Policy
This section outlines the scope of support for various runtime environments in Supabase JavaScript client.
Node.js
We only support Node.js versions that are in Active LTS or Maintenance status as defined by the official Node.js release schedule. This means we support versions that are currently receiving long-term support and critical bug fixes.
When a Node.js version reaches end-of-life and is no longer in Active LTS or Maintenance status, Supabase will drop it in a minor release, and this won't be considered a breaking change.
⚠️ Node.js 18 Deprecation Notice
>
Node.js 18 reached end-of-life on April 30, 2025. As announced in our deprecation notice, support for Node.js 18 was dropped in version 2.79.0.>
If you must use Node.js 18, please use version 2.78.0, which is the last version that supported Node.js 18.Deno
We support Deno versions that are currently receiving active development and security updates. We follow the official Deno release schedule and only support versions from the stable and lts release channels.
When a Deno version reaches end-of-life and is no longer receiving security updates, Supabase will drop it in a minor release, and this won't be considered a breaking change.
Browsers
All modern browsers are supported. We support browsers that provide native fetch API. For Realtime features, browsers must also support native WebSocket API.
Bun
We support Bun runtime environments. Bun provides native fetch support and is compatible with Node.js APIs. Since Bun does not follow a structured release schedule like Node.js or Deno, we support current stable versions of Bun and may drop support for older versions in minor releases without considering it a breaking change.
React Native
We support React Native environments with fetch polyfills provided by the framework. Since React Native does not follow a structured release schedule, we support current stable versions and may drop support for older versions in minor releases without considering it a breaking change.
Cloudflare Workers
We support Cloudflare Workers runtime environments. Cloudflare Workers provides native fetch support. Since Cloudflare Workers does not follow a structured release schedule, we support current stable versions and may drop support for older versions in minor releases without considering it a breaking change.
Important Notes
- Experimental features: Features marked as experimental may be removed or changed without notice
- Build warnings: If you see
UNUSED_EXTERNAL_IMPORTwarnings from Vite/Nuxt, see the supabase-js README — these are false positives
🚀 Quick Start
Installation
npm install @supabase/supabase-jsRead more in each package's README file.
🤝 Contributing
We welcome contributions! Please see our Contributing Guide for details.
Quick Contribution Steps
1. Fork the repository 2. Create a feature branch (git checkout -b feature/amazing-feature) 3. Make your changes and add tests 4. Run tests (npx nx affected --target=test) 5. Commit your changes (npm run commit) 6. Push to your branch (git push origin feature/amazing-feature) 7. Open a Pull Request
Development Guidelines
- Follow conventional commits for commit messages
- Add tests for new functionality
- Update documentation for API changes
- Run
npx nx formatbefore committing - Ensure all tests pass with
npx nx affected --target=test
🧪 Testing
Testing varies per package. See the top-level TESTING.md for an overview and links to package-specific guides.
📚 Documentation
API Documentation
- [Auth SDK](./packages/core/auth-js/README.md) - Authentication and user management
- [Database SDK](./packages/core/postgrest-js/README.md) - Database queries and operations
- [Realtime SDK](./packages/core/realtime-js/README.md) - Real-time subscriptions
- [Storage SDK](./packages/core/storage-js/README.md) - File upload and management
- [Functions SDK](./packages/core/functions-js/README.md) - Edge Functions invocation
- [Main SDK](./packages/core/supabase-js/README.md) - Combined SDK
Architecture Documentation
- [Contributing](./CONTRIBUTING.md) - Development guidelines
- [Release Workflows](./docs/RELEASE.md) - Release and publishing process
- [Migration Guide](./docs/MIGRATION.md) - Migrating to the monorepo structure
- [Security Policy](./docs/SECURITY.md) - Security guidelines and reporting
🔐 Verifying provenance attestations
You can verify registry signatures and provenance attestations for installed packages using the npm CLI:
npm audit signaturesQuick example for a single package install:
npm install @supabase/auth-js
npm audit signaturesExample output:
audited 1 package in 0s
1 package has a verified registry signatureBecause provenance attestations are a new capability, security features may evolve over time. Ensure you are using the latest npm CLI to verify attestation signatures reliably. This may require updating npm beyond the version bundled with Node.js.
📄 License
This project is licensed under the MIT License - see the LICENSE file for details.
🆘 Support
- Documentation: supabase.com/docs
- Community: GitHub Discussions
- Issues: GitHub Issues
- Discord: Supabase Discord
---
<div align="center">
[Website](https://supabase.com) • [Documentation](https://supabase.com/docs) • [Community](https://github.com/supabase/supabase/discussions) • [Twitter](https://twitter.com/supabase)
</div>
<br /> <p align="center"> <a href="https://supabase.io"> <picture> <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/supabase/supabase/master/packages/common/assets/images/supabase-logo-wordmark--dark.svg"> <source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/supabase/supabase/master/packages/common/assets/images/supabase-logo-wordmark--light.svg"> <img alt="Supabase Logo" width="300" src="https://raw.githubusercontent.com/supabase/supabase/master/packages/common/assets/images/logo-preview.jpg"> </picture> </a>
<h1 align="center">Supabase PostgREST JS SDK</h1>
<h3 align="center">Isomorphic JavaScript SDK for <a href="https://postgrest.org">PostgREST</a> with an ORM-like interface.</h3>
<p align="center"> <a href="https://supabase.com/docs/guides/database">Guides</a> · <a href="https://supabase.com/docs/reference/javascript/select">Reference Docs</a> · <a href="https://supabase.github.io/supabase-js/postgrest-js/v2/spec.json">TypeDoc</a> </p> </p>
<div align="center">
   
</div>
Quick start
Install
npm install @supabase/postgrest-jsUsage
import { PostgrestClient } from '@supabase/postgrest-js'
const REST_URL = 'http://localhost:3000'
const postgrest = new PostgrestClient(REST_URL)Custom fetch implementation
postgrest-js uses the `cross-fetch` library to make HTTP requests, but an alternative fetch implementation can be provided as an option. This is most useful in environments where cross-fetch is not compatible, for instance Cloudflare Workers:
import { PostgrestClient } from '@supabase/postgrest-js'
const REST_URL = 'http://localhost:3000'
const postgrest = new PostgrestClient(REST_URL, {
fetch: (...args) => fetch(...args),
})Development
This package is part of the Supabase JavaScript monorepo. To work on this package:
Building
# Build (from monorepo root)
npx nx build postgrest-js
# Build with watch mode for development
npx nx build:watch postgrest-js
# TypeScript type checking
npx nx type-check postgrest-js
# Generate documentation
npx nx docs postgrest-jsTesting
Supabase CLI Required! The postgrest-js tests use the Supabase CLI to run a local PostgreSQL database and PostgREST server.
Quick Start
# Run all tests (from monorepo root)
npx nx test:ci:postgrest postgrest-jsThis single command automatically:
1. Stops any existing Supabase CLI containers 2. Starts PostgreSQL database and PostgREST server via Supabase CLI 3. Resets and seeds the database 4. Runs all Jest unit tests with coverage 5. Cleans up containers
Individual Test Commands
# Run Jest tests with coverage (requires infrastructure running)
npx nx test:run postgrest-js
# Run type tests with tstyche
npx nx test:types postgrest-js
# Run smoke tests (CommonJS and ESM imports)
npx nx test:smoke postgrest-js
# Format code
npx nx format postgrest-js
# Check formatting
npx nx format:check postgrest-jsTest Infrastructure
The tests use Supabase CLI to spin up:
- PostgreSQL - Database with test schema and seed data (port 54322)
- PostgREST - REST API server that the client connects to (port 54321)
# Manually manage test infrastructure (from monorepo root)
npx nx test:infra postgrest-js # Start containers
npx nx test:clean-pre postgrest-js # Stop and remove containersOr directly via Supabase CLI:
cd packages/core/postgrest-js
npx supabase --workdir ./test start # Start all services
npx supabase --workdir ./test db reset # Reset and seed database
npx supabase --workdir ./test stop # Stop all servicesRegenerating TypeScript Types
When the database schema changes, regenerate TypeScript types from the actual database:
# From the monorepo root
npm run codegen:postgrestThis command automatically:
1. Cleans up any existing Supabase containers 2. Starts Supabase (PostgreSQL, PostgREST, and all services) 3. Generates TypeScript types from the database schema 4. Post-processes the generated types (updates JSON type definitions) 5. Formats the generated file with Prettier 6. Cleans up Supabase containers
The generated types are written to test/types.generated.ts.
Test Types Explained
- Unit Tests - Jest tests covering all client functionality (
npx nx test:run postgrest-js) - Type Tests - Validates TypeScript types using tstyche (
npx nx test:types postgrest-js) - Smoke Tests - Basic import/require tests for CommonJS and ESM (
npx nx test:smoke postgrest-js)
Prerequisites
- Supabase CLI must be installed (instructions) or can be used through
npx(npx supabase) - Docker must be installed and running (Supabase CLI uses Docker under the hood)
- Port 54321 - PostgREST API
- Port 54322 - PostgreSQL database
- Port 54323 - Supabase Studio (used for type generation)
PostgREST v12 Backward Compatibility Tests
We maintain backward compatibility tests for PostgREST v12 (the current Supabase CLI uses v14+). These tests ensure the SDK works correctly for users still running older PostgREST versions.
# Run v12 compatibility tests (requires Docker)
npx nx test:ci:v12 postgrest-jsThis command:
1. Starts PostgREST v12 + PostgreSQL in Docker (ports 3012/5433) 2. Runs runtime tests that verify v12-specific behavior 3. Cleans up containers
Type-only tests for v12 compatibility also run as part of the regular type tests:
npx nx test:types postgrest-js # Includes v12-compat.test-d.tsNote: These v12 tests will be removed when v3 ships (sometime in 2026).
Contributing
We welcome contributions! Please see our Contributing Guide for details on how to get started.
For major changes or if you're unsure about something, please open an issue first to discuss your proposed changes.
License
This repo is licensed under MIT License.
<br /> <p align="center"> <a href="https://supabase.io"> <picture> <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/supabase/supabase/master/packages/common/assets/images/supabase-logo-wordmark--dark.svg"> <source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/supabase/supabase/master/packages/common/assets/images/supabase-logo-wordmark--light.svg"> <img alt="Supabase Logo" width="300" src="https://raw.githubusercontent.com/supabase/supabase/master/packages/common/assets/images/logo-preview.jpg"> </picture> </a>
<h1 align="center">Supabase Realtime JS SDK</h1>
<h3 align="center">Send ephemeral messages with <b>Broadcast</b>, track and synchronize state with <b>Presence</b>, and listen to database changes with <b>Postgres Change Data Capture (CDC)</b>.</h3>
<p align="center"> <a href="https://supabase.com/docs/guides/realtime">Guides</a> · <a href="https://supabase.com/docs/reference/javascript">Reference Docs</a> · <a href="https://multiplayer.dev">Multiplayer Demo</a> </p> </p>
<div align="center">
   
</div>
Overview
This SDK enables you to use the following Supabase Realtime's features:
- Broadcast: send ephemeral messages from client to clients with minimal latency. Use cases include sharing cursor positions between users.
- Presence: track and synchronize shared state across clients with the help of CRDTs. Use cases include tracking which users are currently viewing a specific webpage.
- Postgres Change Data Capture (CDC): listen for changes in your PostgreSQL database and send them to clients.
Usage
Installing the Package
npm install @supabase/realtime-jsCreating a Channel
import { RealtimeClient } from '@supabase/realtime-js'
const client = new RealtimeClient(REALTIME_URL, {
params: {
apikey: API_KEY,
},
})
const channel = client.channel('test-channel', {})
channel.subscribe((status, err) => {
if (status === 'SUBSCRIBED') {
console.log('Connected!')
}
if (status === 'CHANNEL_ERROR') {
console.log(`There was an error subscribing to channel: ${err.message}`)
}
if (status === 'TIMED_OUT') {
console.log('Realtime server did not respond in time.')
}
if (status === 'CLOSED') {
console.log('Realtime channel was unexpectedly closed.')
}
})Notes:
REALTIME_URLis'ws://localhost:4000/socket'when developing locally and'wss://<project_ref>.supabase.co/realtime/v1'when connecting to your Supabase project.API_KEYis a JWT whose claims must containexpandrole(existing database role).- Channel name can be any
string. - Setting
privatetotruemeans that the client will use RLS to determine if the user can connect or not to a given channel.
Broadcast
Your client can send and receive messages based on the event.
// Setup...
const channel = client.channel('broadcast-test', { broadcast: { ack: false, self: false } })
channel.on('broadcast', { event: 'some-event' }, (payload) => console.log(payload))
channel.subscribe(async (status) => {
if (status === 'SUBSCRIBED') {
// Send message to other clients listening to 'broadcast-test' channel
await channel.send({
type: 'broadcast',
event: 'some-event',
payload: { hello: 'world' },
})
}
})Notes:
- Setting
acktotruemeans that thechannel.sendpromise will resolve once server replies with acknowledgment that it received the broadcast message request. - Setting
selftotruemeans that the client will receive the broadcast message it sent out.
Broadcast Replay
Broadcast Replay enables private channels to access messages that were sent earlier. Only messages published via Broadcast From the Database are available for replay.
You can configure replay with the following options:
- `since` (Required): The epoch timestamp in milliseconds, specifying the earliest point from which messages should be retrieved.
- `limit` (Optional): The number of messages to return. This must be a positive integer, with a maximum value of 25.
Example:
const twelveHours = 12 * 60 * 60 * 1000
const twelveHoursAgo = Date.now() - twelveHours
const config = { private: true, broadcast: { replay: { since: twelveHoursAgo, limit: 10 } } }
supabase
.channel('main:room', { config })
.on('broadcast', { event: 'my_event' }, (payload) => {
if (payload?.meta?.replayed) {
console.log('This message was sent earlier:', payload)
} else {
console.log('This is a new message', payload)
}
// ...
})
.subscribe()Presence
Your client can track and sync state that's stored in the channel.
// Setup...
const channel = client.channel('presence-test', {
config: {
presence: {
key: '',
},
},
})
channel.on('presence', { event: 'sync' }, () => {
console.log('Online users: ', channel.presenceState())
})
channel.on('presence', { event: 'join' }, ({ newPresences }) => {
console.log('New users have joined: ', newPresences)
})
channel.on('presence', { event: 'leave' }, ({ leftPresences }) => {
console.log('Users have left: ', leftPresences)
})
channel.subscribe(async (status) => {
if (status === 'SUBSCRIBED') {
const status = await channel.track({ user_id: 1 })
console.log(status)
}
})Postgres CDC
Receive database changes on the client.
// Setup...
const channel = client.channel('db-changes')
channel.on('postgres_changes', { event: '*', schema: 'public' }, (payload) => {
console.log('All changes in public schema: ', payload)
})
channel.on(
'postgres_changes',
{ event: 'INSERT', schema: 'public', table: 'messages' },
(payload) => {
console.log('All inserts in messages table: ', payload)
}
)
channel.on(
'postgres_changes',
{ event: 'UPDATE', schema: 'public', table: 'users', filter: 'username=eq.Realtime' },
(payload) => {
console.log('All updates on users table when username is Realtime: ', payload)
}
)
channel.subscribe(async (status) => {
if (status === 'SUBSCRIBED') {
console.log('Ready to receive database changes!')
}
})Get All Channels
You can see all the channels that your client has instantiatied.
// Setup...
client.getChannels()Cleanup
It is highly recommended that you clean up your channels after you're done with them.
- Remove a single channel
// Setup...
const channel = client.channel('some-channel-to-remove')
channel.unsubscribe()
client.removeChannel(channel)- Remove all channels and close the connection
// Setup...
client.removeAllChannels()
client.disconnect()Development
This package is part of the Supabase JavaScript monorepo. To work on this package:
Building
# Complete build (from monorepo root)
npx nx build realtime-js
# Build with watch mode for development
npx nx build realtime-js --watch
# Individual build targets
npx nx build:main realtime-js # CommonJS build (dist/main/)
npx nx build:module realtime-js # ES Modules build (dist/module/)
# Other useful commands
npx nx clean realtime-js # Clean build artifacts
npx nx lint realtime-js # Run ESLint
npx nx typecheck realtime-js # TypeScript type checkingBuild Outputs
- CommonJS (`dist/main/`) - For Node.js environments
- ES Modules (`dist/module/`) - For modern bundlers (Webpack, Vite, Rollup)
- TypeScript definitions (`dist/module/index.d.ts`) - Type definitions for TypeScript projects
Note: Unlike some other packages, realtime-js doesn't include a UMD build since it's primarily used in Node.js or bundled applications.
Validating Package Exports
# Check if package exports are correctly configured
npx nx check-exports realtime-jsThis command uses "Are the types wrong?" to verify that the package exports work correctly in different environments. Run this before publishing to ensure your package can be imported correctly by all consumers.
Testing
No Docker or Supabase instance required! The realtime-js tests use mocked WebSocket connections, so they're completely self-contained.
# Run unit tests (from monorepo root)
npx nx test realtime-js
# Run tests with coverage report
npx nx test:coverage realtime-js
# Run tests in watch mode during development
npx nx test:watch realtime-jsTest Scripts Explained
- test - Runs all unit tests once using Vitest
- test:coverage - Runs tests and generates coverage report with terminal output
- test:watch - Runs tests in interactive watch mode for development
The tests mock WebSocket connections using mock-socket, so you can run them anytime without any external dependencies.
Contributing
We welcome contributions! Please see our Contributing Guide for details on how to get started.
For major changes or if you're unsure about something, please open an issue first to discuss your proposed changes.
Credits
This repo draws heavily from phoenix-js.
License
MIT.
<br /> <p align="center"> <a href="https://supabase.io"> <picture> <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/supabase/supabase/master/packages/common/assets/images/supabase-logo-wordmark--dark.svg"> <source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/supabase/supabase/master/packages/common/assets/images/supabase-logo-wordmark--light.svg"> <img alt="Supabase Logo" width="300" src="https://raw.githubusercontent.com/supabase/supabase/master/packages/common/assets/images/logo-preview.jpg"> </picture> </a>
<h1 align="center">Supabase Storage JS SDK</h1>
<h3 align="center">JavaScript SDK to interact with Supabase Storage, including file storage and vector embeddings.</h3>
<p align="center"> <a href="https://supabase.com/docs/guides/storage">Guides</a> · <a href="https://supabase.com/docs/reference/javascript/storage-createbucket">Reference Docs</a> · <a href="https://supabase.github.io/supabase-js/storage-js/v2/spec.json">TypeDoc</a> </p> </p>
<div align="center">
   
</div>
Requirements
- Node.js 20 or later (Node.js 18 support dropped as of October 31, 2025)
- For browser support, all modern browsers are supported
⚠️ Node.js 18 Deprecation Notice
>
Node.js 18 reached end-of-life on April 30, 2025. As announced in our deprecation notice, support for Node.js 18 was dropped on October 31, 2025.
Features
- File Storage: Upload, download, list, move, and delete files
- Access Control: Public and private buckets with fine-grained permissions
- Signed URLs: Generate time-limited URLs for secure file access
- Image Transformations: On-the-fly image resizing and optimization
- Vector Embeddings: Store and query high-dimensional embeddings with similarity search
- Analytics Buckets: Iceberg table-based buckets optimized for analytical queries and data processing
Quick Start Guide
Installing the module
npm install @supabase/storage-jsConnecting to the storage backend
There are two ways to use the Storage SDK:
Option 1: Via Supabase Client (Recommended)
If you're already using @supabase/supabase-js, access storage through the client:
import { createClient } from '@supabase/supabase-js'
// Use publishable/anon key for frontend applications
const supabase = createClient('https://<project_ref>.supabase.co', '<your-publishable-key>')
// Access storage
const storage = supabase.storage
// Access different bucket types
const regularBucket = storage.from('my-bucket')
const vectorBucket = storage.vectors.from('embeddings-bucket')
const analyticsBucket = storage.analytics // Analytics APIOption 2: Standalone StorageClient
For backend applications or when you need to bypass Row Level Security:
import { StorageClient } from '@supabase/storage-js'
const STORAGE_URL = 'https://<project_ref>.supabase.co/storage/v1'
const SERVICE_KEY = '<your-secret-key>' // Use secret key for backend operations
const storageClient = new StorageClient(STORAGE_URL, {
apikey: SERVICE_KEY,
Authorization: `Bearer ${SERVICE_KEY}`,
})
// Access different bucket types
const regularBucket = storageClient.from('my-bucket')
const vectorBucket = storageClient.vectors.from('embeddings-bucket')
const analyticsBucket = storageClient.analytics // Analytics APIWhen to use each approach:
>
- Use supabase.storage when working with other Supabase features (auth, database, etc.) in frontend applications- Use new StorageClient() for backend applications, Edge Functions, or when you need to bypass RLS policiesNote: Refer to the Storage Access Control guide for detailed information on creating RLS policies.
Understanding Bucket Types
Supabase Storage supports three types of buckets, each optimized for different use cases:
1. Regular Storage Buckets (File Storage)
Standard buckets for storing files, images, videos, and other assets.
// Create regular storage bucket
const { data, error } = await storageClient.createBucket('my-files', {
public: false,
})
// Upload files
await storageClient.from('my-files').upload('avatar.png', file)Use cases: User uploads, media assets, documents, backups
2. Vector Buckets (Embeddings Storage)
Specialized buckets for storing and querying high-dimensional vector embeddings.
// Create vector bucket
await storageClient.vectors.createBucket('embeddings-prod')
// Create index and insert vectors
const bucket = storageClient.vectors.from('embeddings-prod')
await bucket.createIndex({
indexName: 'documents',
dimension: 1536,
distanceMetric: 'cosine',
})Use cases: Semantic search, AI-powered recommendations, similarity matching
[See full Vector Embeddings documentation below](#vector-embeddings)
3. Analytics Buckets
Specialized buckets using Apache Iceberg table format, optimized for analytical queries and large-scale data processing.
// Create analytics bucket
await storageClient.analytics.createBucket('analytics-data')
// List analytics buckets
const { data, error } = await storageClient.analytics.listBuckets()
// Delete analytics bucket
await storageClient.analytics.deleteBucket('analytics-data')Use cases: Time-series data, analytical queries, data lakes, large-scale data processing, business intelligence
[See full Analytics Buckets documentation below](#analytics-buckets)
---
Handling resources
Handling Storage Buckets
- Create a new Storage bucket:
const { data, error } = await storageClient.createBucket(
'test_bucket', // Bucket name (must be unique)
{ public: false } // Bucket options
)- Retrieve the details of an existing Storage bucket:
const { data, error } = await storageClient.getBucket('test_bucket')- Update a new Storage bucket:
const { data, error } = await storageClient.updateBucket(
'test_bucket', // Bucket name
{ public: false } // Bucket options
)- Remove all objects inside a single bucket:
const { data, error } = await storageClient.emptyBucket('test_bucket')- Delete an existing bucket (a bucket can't be deleted with existing objects inside it):
const { data, error } = await storageClient.deleteBucket('test_bucket')- Retrieve the details of all Storage buckets within an existing project:
// List all buckets
const { data, error } = await storageClient.listBuckets()
// List buckets with options (pagination, sorting, search)
const { data, error } = await storageClient.listBuckets({
limit: 10,
offset: 0,
sortColumn: 'created_at',
sortOrder: 'desc',
search: 'prod',
})Handling Files
- Upload a file to an existing bucket:
const fileBody = ... // load your file here
const { data, error } = await storageClient.from('bucket').upload('path/to/file', fileBody)Note:
The path indata.Keyis prefixed by the bucket ID and is not the value which should be passed to thedownloadmethod in order to fetch the file.
To fetch the file via thedownloadmethod, usedata.pathanddata.bucketIdas follows:
>
```javascript
const { data, error } = await storageClient.from('bucket').upload('/folder/file.txt', fileBody)
// check for errors
const { data2, error2 } = await storageClient.from(data.bucketId).download(data.path)
```
Note: The upload method also accepts a map of optional parameters. For a complete list see the Supabase API reference.- Download a file from an exisiting bucket:
const { data, error } = await storageClient.from('bucket').download('path/to/file')- List all the files within a bucket:
const { data, error } = await storageClient.from('bucket').list('folder')Note: The list method also accepts a map of optional parameters. For a complete list see the Supabase API reference.- Replace an existing file at the specified path with a new one:
const fileBody = ... // load your file here
const { data, error } = await storageClient
.from('bucket')
.update('path/to/file', fileBody)Note: The upload method also accepts a map of optional parameters. For a complete list see the Supabase API reference.- Move an existing file:
const { data, error } = await storageClient
.from('bucket')
.move('old/path/to/file', 'new/path/to/file')- Delete files within the same bucket:
const { data, error } = await storageClient.from('bucket').remove(['path/to/file'])- Create signed URL to download file without requiring permissions:
const expireIn = 60
const { data, error } = await storageClient
.from('bucket')
.createSignedUrl('path/to/file', expireIn)- Retrieve URLs for assets in public buckets:
const { data, error } = await storageClient.from('public-bucket').getPublicUrl('path/to/file')Analytics Buckets
Supabase Storage provides specialized analytics buckets using Apache Iceberg table format, optimized for analytical workloads and large-scale data processing. These buckets are designed for data lake architectures, time-series data, and business intelligence applications.
What are Analytics Buckets?
Analytics buckets use the Apache Iceberg open table format, providing:
- ACID transactions for data consistency
- Schema evolution without data rewrites
- Time travel to query historical data
- Efficient metadata management for large datasets
- Optimized for analytical queries rather than individual file operations
When to Use Analytics Buckets
Use analytics buckets for:
- Time-series data (logs, metrics, events)
- Data lake architectures
- Business intelligence and reporting
- Large-scale batch processing
- Analytical workloads requiring ACID guarantees
Use regular storage buckets for:
- User file uploads (images, documents, videos)
- Individual file management
- Content delivery
- Simple object storage needs
Quick Start
You can access analytics functionality through the analytics property on your storage client:
Via Supabase Client
import { createClient } from '@supabase/supabase-js'
const supabase = createClient('https://your-project.supabase.co', 'your-publishable-key')
// Access analytics operations
const analytics = supabase.storage.analytics
// Create an analytics bucket
const { data, error } = await analytics.createBucket('analytics-data')
if (error) {
console.error('Failed to create analytics bucket:', error.message)
} else {
console.log('Created bucket:', data.name)
}Via StorageClient
import { StorageClient } from '@supabase/storage-js'
const storageClient = new StorageClient('https://your-project.supabase.co/storage/v1', {
apikey: 'YOUR_API_KEY',
Authorization: 'Bearer YOUR_TOKEN',
})
// Access analytics operations
const analytics = storageClient.analytics
// Create an analytics bucket
await analytics.createBucket('analytics-data')API Reference
Create Analytics Bucket
Creates a new analytics bucket using Iceberg table format:
const { data, error } = await analytics.createBucket('my-analytics-bucket')
if (error) {
console.error('Error:', error.message)
} else {
console.log('Created bucket:', data)
}Returns:
{
data: {
id: string
type: 'ANALYTICS'
format: string
created_at: string
updated_at: string
} | null
error: StorageError | null
}List Analytics Buckets
Retrieves all analytics buckets in your project with optional filtering and pagination:
const { data, error } = await analytics.listBuckets({
limit: 10,
offset: 0,
sortColumn: 'created_at',
sortOrder: 'desc',
search: 'prod',
})
if (data) {
console.log(`Found ${data.length} analytics buckets`)
data.forEach((bucket) => {
console.log(`- ${bucket.id} (created: ${bucket.created_at})`)
})
}Parameters:
limit?: number- Maximum number of buckets to returnoffset?: number- Number of buckets to skip (for pagination)sortColumn?: 'id' | 'name' | 'created_at' | 'updated_at'- Column to sort bysortOrder?: 'asc' | 'desc'- Sort directionsearch?: string- Search term to filter bucket names
Returns:
{
data: AnalyticBucket[] | null
error: StorageError | null
}Example with Pagination:
// Fetch first page
const firstPage = await analytics.listBuckets({
limit: 100,
offset: 0,
sortColumn: 'created_at',
sortOrder: 'desc',
})
// Fetch second page
const secondPage = await analytics.listBuckets({
limit: 100,
offset: 100,
sortColumn: 'created_at',
sortOrder: 'desc',
})Delete Analytics Bucket
Deletes an analytics bucket. The bucket must be empty before deletion.
const { data, error } = await analytics.deleteBucket('old-analytics-bucket')
if (error) {
console.error('Failed to delete:', error.message)
} else {
console.log('Bucket deleted:', data.message)
}Returns:
{
data: { message: string } | null
error: StorageError | null
}Note: A bucket cannot be deleted if it contains data. You must empty the bucket first.
Get Iceberg Catalog for Advanced Operations
For advanced operations like creating tables, namespaces, and querying Iceberg metadata, use the from() method to get a configured iceberg-js client:
// Get an Iceberg REST Catalog client for your analytics bucket
const catalog = analytics.from('analytics-data')
// Create a namespace
await catalog.createNamespace({ namespace: ['default'] }, { properties: { owner: 'data-team' } })
// Create a table with schema
await catalog.createTable(
{ namespace: ['default'] },
{
name: 'events',
schema: {
type: 'struct',
fields: [
{ id: 1, name: 'id', type: 'long', required: true },
{ id: 2, name: 'timestamp', type: 'timestamp', required: true },
{ id: 3, name: 'user_id', type: 'string', required: false },
],
'schema-id': 0,
'identifier-field-ids': [1],
},
'partition-spec': {
'spec-id': 0,
fields: [],
},
'write-order': {
'order-id': 0,
fields: [],
},
properties: {
'write.format.default': 'parquet',
},
}
)
// List tables in namespace
const tables = await catalog.listTables({ namespace: ['default'] })
console.log(tables) // [{ namespace: ['default'], name: 'events' }]
// Load table metadata
const table = await catalog.loadTable({ namespace: ['default'], name: 'events' })
// Update table properties
await catalog.updateTable(
{ namespace: ['default'], name: 'events' },
{ properties: { 'read.split.target-size': '134217728' } }
)
// Drop table
await catalog.dropTable({ namespace: ['default'], name: 'events' })
// Drop namespace
await catalog.dropNamespace({ namespace: ['default'] })Returns: IcebergRestCatalog instance from iceberg-js
Note: The from() method returns an Iceberg REST Catalog client that provides full access to the Apache Iceberg REST API. For complete documentation of available operations, see the iceberg-js documentation.Error Handling
Analytics buckets use the same error handling pattern as the rest of the Storage SDK:
const { data, error } = await analytics.createBucket('my-bucket')
if (error) {
console.error('Error:', error.message)
console.error('Status:', error.status)
console.error('Status Code:', error.statusCode)
// Handle error appropriately
}Throwing Errors
You can configure the client to throw errors instead of returning them:
const analytics = storageClient.analytics
analytics.throwOnError()
try {
const { data } = await analytics.createBucket('my-bucket')
// data is guaranteed to be present
console.log('Success:', data)
} catch (error) {
if (error instanceof StorageApiError) {
console.error('API Error:', error.statusCode, error.message)
}
}TypeScript Types
The library exports TypeScript types for analytics buckets:
import type { AnalyticBucket, BucketType, StorageError } from '@supabase/storage-js'
// AnalyticBucket type
interface AnalyticBucket {
id: string
type: 'ANALYTICS'
format: string
created_at: string
updated_at: string
}Common Patterns
Checking if a Bucket Exists
async function bucketExists(bucketName: string): Promise<boolean> {
const { data, error } = await analytics.listBuckets({
search: bucketName,
})
if (error) {
console.error('Error checking bucket:', error.message)
return false
}
return data?.some((bucket) => bucket.id === bucketName) ?? false
}Creating Bucket with Error Handling
async function ensureAnalyticsBucket(bucketName: string) {
// Try to create the bucket
const { data, error } = await analytics.createBucket(bucketName)
if (error) {
// Check if bucket already exists (conflict error)
if (error.statusCode === '409') {
console.log(`Bucket '${bucketName}' already exists`)
return { success: true, created: false }
}
// Other error occurred
console.error('Failed to create bucket:', error.message)
return { success: false, error }
}
console.log(`Created new bucket: '${bucketName}'`)
return { success: true, created: true, data }
}Listing All Buckets with Pagination
async function getAllAnalyticsBuckets() {
const allBuckets: AnalyticBucket[] = []
let offset = 0
const limit = 100
while (true) {
const { data, error } = await analytics.listBuckets({
limit,
offset,
sortColumn: 'created_at',
sortOrder: 'desc',
})
if (error) {
console.error('Error fetching buckets:', error.message)
break
}
if (!data || data.length === 0) {
break
}
allBuckets.push(...data)
// If we got fewer results than the limit, we've reached the end
if (data.length < limit) {
break
}
offset += limit
}
return allBuckets
}Vector Embeddings
Supabase Storage provides built-in support for storing and querying high-dimensional vector embeddings, powered by S3 Vectors. This enables semantic search, similarity matching, and AI-powered applications without needing a separate vector database.
Note: Vector embeddings functionality is available in @supabase/storage-js v2.76 and later.Features
- Vector Buckets: Organize vector indexes into logical containers
- Vector Indexes: Define schemas with configurable dimensions and distance metrics
- Batch Operations: Insert/update/delete up to 500 vectors per request
- Similarity Search: Query for nearest neighbors using cosine, euclidean, or dot product distance
- Metadata Filtering: Store and filter vectors by arbitrary JSON metadata
- Pagination: Efficiently scan large vector datasets
- Parallel Scanning: Distribute scans across multiple workers for high throughput
- Cross-platform: Works in Node.js, browsers, and edge runtimes
Quick Start
You can access vector functionality in three ways, depending on your use case:
Option 1: Via Supabase Client (Most Common)
If you're using the full Supabase client:
import { createClient } from '@supabase/supabase-js'
const supabase = createClient('https://your-project.supabase.co', 'your-publishable-key')
// Access vector operations through storage
const vectors = supabase.storage.vectors
// Create a vector bucket
await vectors.createBucket('embeddings-prod')
// Create an index
const bucket = vectors.from('embeddings-prod')
await bucket.createIndex({
indexName: 'documents-openai',
dataType: 'float32',
dimension: 1536,
distanceMetric: 'cosine',
})
// Insert vectors
const index = bucket.index('documents-openai')
await index.putVectors({
vectors: [
{
key: 'doc-1',
data: { float32: [0.1, 0.2, 0.3 /* ...1536 dimensions */] },
metadata: { title: 'Introduction', category: 'docs' },
},
],
})
// Query similar vectors
const { data, error } = await index.queryVectors({
queryVector: { float32: [0.15, 0.25, 0.35 /* ...1536 dimensions */] },
topK: 5,
returnDistance: true,
returnMetadata: true,
})
if (data) {
data.matches.forEach((match) => {
console.log(`${match.key}: distance=${match.distance}`)
console.log('Metadata:', match.metadata)
})
}Option 2: Via StorageClient
If you're using the standalone StorageClient for storage operations, access vectors through the vectors property:
import { StorageClient } from '@supabase/storage-js'
const storageClient = new StorageClient('https://your-project.supabase.co/storage/v1', {
apikey: 'YOUR_API_KEY',
Authorization: 'Bearer YOUR_TOKEN',
})
// Access vector operations
const vectors = storageClient.vectors
// Use the same API as shown in Option 1
await vectors.createBucket('embeddings-prod')
const bucket = vectors.from('embeddings-prod')
// ... rest of operationsOption 3: Standalone Vector Client
For vector-only applications that don't need regular file storage operations:
import { StorageVectorsClient } from '@supabase/storage-js'
// Initialize standalone vector client
const vectorClient = new StorageVectorsClient('https://your-project.supabase.co/storage/v1', {
headers: { Authorization: 'Bearer YOUR_TOKEN' },
})
// Use the same API as shown in Option 1
await vectorClient.createBucket('embeddings-prod')
const bucket = vectorClient.from('embeddings-prod')
// ... rest of operationsWhen to use each approach:
>
- Option 1: When using other Supabase features (auth, database, realtime)
- Option 2: When working with both file storage and vectors
- Option 3: For dedicated vector-only applications without file storage
API Reference
Client Initialization
const vectorClient = new StorageVectorsClient(url, options?)Options:
headers?: Record<string, string>- Custom HTTP headers (e.g., Authorization)fetch?: Fetch- Custom fetch implementation
Vector Buckets
Vector buckets are top-level containers for organizing vector indexes.
Create Bucket
const { data, error } = await vectorClient.createBucket('my-bucket')Get Bucket
const { data, error } = await vectorClient.getBucket('my-bucket')
console.log('Created at:', new Date(data.vectorBucket.creationTime! * 1000))List Buckets
const { data, error } = await vectorClient.listBuckets({
prefix: 'prod-',
maxResults: 100,
})
// Pagination
if (data?.nextToken) {
const next = await vectorClient.listBuckets({ nextToken: data.nextToken })
}Delete Bucket
// Bucket must be empty (all indexes deleted first)
const { error } = await vectorClient.deleteBucket('my-bucket')Vector Indexes
Vector indexes define the schema for embeddings including dimension and distance metric.
Create Index
const bucket = vectorClient.from('my-bucket')
await bucket.createIndex({
indexName: 'my-index',
dataType: 'float32',
dimension: 1536,
distanceMetric: 'cosine', // 'cosine' | 'euclidean' | 'dotproduct'
metadataConfiguration: {
nonFilterableMetadataKeys: ['raw_text', 'internal_id'],
},
})Distance Metrics:
cosine- Cosine similarity (normalized dot product)euclidean- Euclidean distance (L2 norm)dotproduct- Dot product similarity
Get Index
const { data, error } = await bucket.getIndex('my-index')
console.log('Dimension:', data?.index.dimension)
console.log('Distance metric:', data?.index.distanceMetric)List Indexes
const { data, error } = await bucket.listIndexes({
prefix: 'documents-',
maxResults: 100,
})Delete Index
// Deletes index and all its vectors
await bucket.deleteIndex('my-index')Vector Operations
Insert/Update Vectors (Upsert)
const index = vectorClient.from('my-bucket').index('my-index')
await index.putVectors({
vectors: [
{
key: 'unique-id-1',
data: {
float32: [
/* 1536 numbers */
],
},
metadata: {
title: 'Document Title',
category: 'technical',
page: 1,
},
},
// ... up to 500 vectors per request
],
})Limitations:
- 1-500 vectors per request
- Vectors must match index dimension
- Keys must be unique within index
Get Vectors by Key
const { data, error } = await index.getVectors({
keys: ['doc-1', 'doc-2', 'doc-3'],
returnData: true, // Include embeddings
returnMetadata: true, // Include metadata
})
data?.vectors.forEach((v) => {
console.log(v.key, v.metadata)
})Query Similar Vectors (ANN Search)
const { data, error } = await index.queryVectors({
queryVector: {
float32: [
/* 1536 numbers */
],
},
topK: 10,
filter: {
category: 'technical',
published: true,
},
returnDistance: true,
returnMetadata: true,
})
// Results ordered by similarity
data?.matches.forEach((match) => {
console.log(`${match.key}: distance=${match.distance}`)
})Filter Syntax: The filter parameter accepts arbitrary JSON for metadata filtering. Non-filterable keys (configured at index creation) cannot be used in filters but can still be returned.
List/Scan Vectors
// Simple pagination
let nextToken: string | undefined
do {
const { data } = await index.listVectors({
maxResults: 500,
nextToken,
returnMetadata: true,
})
console.log('Batch:', data?.vectors.length)
nextToken = data?.nextToken
} while (nextToken)
// Parallel scanning (4 workers)
const workers = [0, 1, 2, 3].map(async (segmentIndex) => {
const { data } = await index.listVectors({
segmentCount: 4,
segmentIndex,
returnMetadata: true,
})
return data?.vectors || []
})
const results = await Promise.all(workers)
const allVectors = results.flat()Limitations:
maxResults: 1-1000 (default: 500)segmentCount: 1-16- Response may be limited by 1MB size
Delete Vectors
await index.deleteVectors({
keys: ['doc-1', 'doc-2', 'doc-3'],
// ... up to 500 keys per request
})Error Handling
The library uses a consistent error handling pattern:
const { data, error } = await vectorClient.createBucket('my-bucket')
if (error) {
console.error('Error:', error.message)
console.error('Status:', error.status)
console.error('Code:', error.statusCode)
}Error Codes
| Code | HTTP | Description |
|---|---|---|
InternalError | 500 | Internal server error |
S3VectorConflictException | 409 | Resource already exists |
S3VectorNotFoundException | 404 | Resource not found |
S3VectorBucketNotEmpty | 400 | Bucket contains indexes |
S3VectorMaxBucketsExceeded | 400 | Bucket quota exceeded |
S3VectorMaxIndexesExceeded | 400 | Index quota exceeded |
Throwing Errors
You can configure the client to throw errors instead:
const vectorClient = new StorageVectorsClient(url, options)
vectorClient.throwOnError()
try {
const { data } = await vectorClient.createBucket('my-bucket')
// data is guaranteed to be present
} catch (error) {
if (error instanceof StorageVectorsApiError) {
console.error('API Error:', error.statusCode)
}
}Advanced Usage
Scoped Clients
Create scoped clients for cleaner code:
// Bucket-scoped operations
const bucket = vectorClient.from('embeddings-prod')
await bucket.createIndex({
/* ... */
})
await bucket.listIndexes()
// Index-scoped operations
const index = bucket.index('documents-openai')
await index.putVectors({
/* ... */
})
await index.queryVectors({
/* ... */
})Custom Fetch
Provide a custom fetch implementation:
import { StorageVectorsClient } from '@supabase/storage-js'
const vectorClient = new StorageVectorsClient(url, {
fetch: customFetch,
headers: {
/* ... */
},
})Batch Processing
Process large datasets in batches:
async function insertLargeDataset(vectors: VectorObject[]) {
const batchSize = 500
for (let i = 0; i < vectors.length; i += batchSize) {
const batch = vectors.slice(i, i + batchSize)
await index.putVectors({ vectors: batch })
console.log(`Inserted ${i + batch.length}/${vectors.length}`)
}
}Float32 Validation
Ensure vectors are properly normalized to float32:
import { normalizeToFloat32 } from '@supabase/storage-js'
const vector = normalizeToFloat32([0.1, 0.2, 0.3 /* ... */])Type Definitions
The library exports comprehensive TypeScript types:
import type {
VectorBucket,
VectorIndex,
VectorData,
VectorObject,
VectorMatch,
VectorMetadata,
DistanceMetric,
ApiResponse,
StorageVectorsError,
} from '@supabase/storage-js'Development
This package is part of the Supabase JavaScript monorepo. To work on this package:
Building
Build Scripts Overview
# Build the package
npx nx build storage-js
# Watch mode for development
npx nx build storage-js --watch
# Generate documentation
npx nx docs storage-jsTesting
Important: The storage-js tests require a local Supabase stack running via the Supabase CLI. Docker must be running since the Supabase CLI uses it internally.
Prerequisites
1. Docker must be installed and running (used by Supabase CLI internally) 2. Supabase CLI — installed automatically via npx supabase
Test Scripts Overview
| Script | Description | What it does |
|---|---|---|
test:storage | Complete test workflow | Runs the full test cycle: clean → start infra → run tests → clean |
test:suite | Jest tests only | Runs Jest tests with coverage (requires infra to be running) |
test:infra | Start test infrastructure | Starts Supabase CLI stack (PostgreSQL, Storage API, Kong, etc.) |
test:clean-post | Stop and clean infrastructure | Stops the Supabase CLI stack |
Running Tests
Option 1: Complete Test Run (Recommended)
This handles everything automatically - starting infrastructure, running tests, and cleaning up:
# From monorepo root
npx nx test:storage storage-jsThis command will:
1. Stop any existing test containers 2. Build and start fresh test infrastructure 3. Wait for services to be ready 4. Run all Jest tests with coverage 5. Clean up all containers after tests complete
Option 2: Manual Infrastructure Management
Useful for development when you want to run tests multiple times without restarting Docker:
# Step 1: Start the test infrastructure
# From root
npx nx test:infra storage-js
# This starts: PostgreSQL, Storage API, Kong Gateway, and imgproxy
# Step 2: Run tests (can run multiple times)
npx nx test:suite storage-js
# Step 3: When done, clean up the infrastructure
npx nx test:clean-post storage-jsOption 3: Development Mode
For actively developing and debugging tests:
# Start infrastructure once (from root)
npx nx test:infra storage-js
# Run tests in watch mode
npx nx test:suite storage-js --watch
# Clean up when done
npx nx test:clean-post storage-jsTest Infrastructure Details
The test infrastructure is managed via the Supabase CLI (npx supabase start --workdir test), which starts a local Supabase stack defined by the config in test/. This includes PostgreSQL, the Storage API, Kong Gateway, and supporting services.
Common Issues and Solutions
| Issue | Solution |
|---|---|
| Port conflicts | Another service is using a required port. Run npx nx test:clean-post storage-js then try again |
| "request failed, reason:" errors | Infrastructure isn't running. Run npx nx test:infra storage-js first |
| Tests fail with connection errors | Ensure Docker is running (Supabase CLI requires Docker) |
| Stack already running | Run npx nx test:clean-post storage-js to stop it before restarting |
Understanding Test Failures
- StorageUnknownError with "request failed": Infrastructure not running
- Snapshot failures: Expected test data has changed — review and update snapshots if needed
Contributing
We welcome contributions! Please see our Contributing Guide for details on how to get started.
For major changes or if you're unsure about something, please open an issue first to discuss your proposed changes.
<br /> <p align="center"> <a href="https://supabase.io"> <picture> <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/supabase/supabase/master/packages/common/assets/images/supabase-logo-wordmark--dark.svg"> <source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/supabase/supabase/master/packages/common/assets/images/supabase-logo-wordmark--light.svg"> <img alt="Supabase Logo" width="300" src="https://raw.githubusercontent.com/supabase/supabase/master/packages/common/assets/images/logo-preview.jpg"> </picture> </a>
<h1 align="center">Supabase JS SDK</h1>
<h3 align="center">Isomorphic JavaScript SDK for Supabase - combining Auth, Database, Storage, Functions, and Realtime.</h3>
<p align="center"> <a href="https://supabase.com/docs/guides/getting-started">Guides</a> · <a href="https://supabase.com/docs/reference/javascript/start">Reference Docs</a> · <a href="https://supabase.github.io/supabase-js/supabase-js/v2/spec.json">TypeDoc</a> </p> </p>
<div align="center">
   
</div>
Usage
First of all, you need to install the library:
npm install @supabase/supabase-jsThen you're able to import the library and establish the connection with the database:
import { createClient } from '@supabase/supabase-js'
// Create a single supabase client for interacting with your database
const supabase = createClient('https://xyzcompany.supabase.co', 'public-anon-key')UMD
You can use plain <script>s to import supabase-js from CDNs, like:
<script src="https://cdn.jsdelivr.net/npm/@supabase/supabase-js@2"></script>or even:
<script src="https://unpkg.com/@supabase/supabase-js@2"></script>Then you can use it from a global supabase variable:
<script>
const { createClient } = supabase
const _supabase = createClient('https://xyzcompany.supabase.co', 'public-anon-key')
console.log('Supabase Instance: ', _supabase)
// ...
</script>ESM
You can use <script type="module"> to import supabase-js from CDNs, like:
<script type="module">
import { createClient } from 'https://cdn.jsdelivr.net/npm/@supabase/supabase-js/+esm'
const supabase = createClient('https://xyzcompany.supabase.co', 'public-anon-key')
console.log('Supabase Instance: ', supabase)
// ...
</script>Deno
You can use supabase-js in the Deno runtime via JSR:
import { createClient } from 'jsr:@supabase/supabase-js@2'Custom fetch implementation
supabase-js uses the `cross-fetch` library to make HTTP requests, but an alternative fetch implementation can be provided as an option. This is most useful in environments where cross-fetch is not compatible, for instance Cloudflare Workers:
import { createClient } from '@supabase/supabase-js'
// Provide a custom `fetch` implementation as an option
const supabase = createClient('https://xyzcompany.supabase.co', 'public-anon-key', {
global: {
fetch: (...args) => fetch(...args),
},
})Support Policy
This section outlines the scope of support for various runtime environments in Supabase JavaScript client.
Node.js
We only support Node.js versions that are in Active LTS or Maintenance status as defined by the official Node.js release schedule. This means we support versions that are currently receiving long-term support and critical bug fixes.
When a Node.js version reaches end-of-life and is no longer in Active LTS or Maintenance status, Supabase will drop it in a minor release, and this won't be considered a breaking change.
⚠️ Node.js 18 Deprecation Notice
>
Node.js 18 reached end-of-life on April 30, 2025. As announced in our deprecation notice, support for Node.js 18 was dropped in version 2.79.0.>
If you must use Node.js 18, please use version 2.78.0, which is the last version that supported Node.js 18.Deno
We support Deno versions that are currently receiving active development and security updates. We follow the official Deno release schedule and only support versions from the stable and lts release channels.
When a Deno version reaches end-of-life and is no longer receiving security updates, Supabase will drop it in a minor release, and this won't be considered a breaking change.
Browsers
All modern browsers are supported. We support browsers that provide native fetch API. For Realtime features, browsers must also support native WebSocket API.
Bun
We support Bun runtime environments. Bun provides native fetch support and is compatible with Node.js APIs. Since Bun does not follow a structured release schedule like Node.js or Deno, we support current stable versions of Bun and may drop support for older versions in minor releases without considering it a breaking change.
React Native
We support React Native environments with fetch polyfills provided by the framework. Since React Native does not follow a structured release schedule, we support current stable versions and may drop support for older versions in minor releases without considering it a breaking change.
Cloudflare Workers
We support Cloudflare Workers runtime environments. Cloudflare Workers provides native fetch support. Since Cloudflare Workers does not follow a structured release schedule, we support current stable versions and may drop support for older versions in minor releases without considering it a breaking change.
Important Notes
- Experimental features: Features marked as experimental may be removed or changed without notice
Known Build Warnings
UNUSED_EXTERNAL_IMPORT in Vite / Rollup / Nuxt
When bundling your app, you may see warnings like:
"PostgrestError" is imported from external module "@supabase/postgrest-js" but never used in "...supabase-js/dist/index.mjs".
"FunctionRegion", "FunctionsError", "FunctionsFetchError", "FunctionsHttpError" and "FunctionsRelayError" are imported from external module "@supabase/functions-js" but never used in "...".This is a false positive — your bundle is fine. Here is why it happens:
@supabase/supabase-js re-exports PostgrestError, FunctionsError, and related symbols so you can import them directly from @supabase/supabase-js. However, our build tool merges all imports from the same package into a single import statement in the built output:
// dist/index.mjs (simplified)
import { PostgrestClient, PostgrestError } from '@supabase/postgrest-js'
// ^ used internally ^ re-exported for youYour bundler checks which names from that import are used _in the code body_, and flags PostgrestError as unused because it only appears in an export statement — not called or assigned. The export itself is the usage, but downstream bundlers don't track this correctly. This is a known Rollup/Vite limitation with re-exported external imports.
Nothing is broken. Tree-shaking and bundle size are unaffected.
To suppress the warning:
Vite / Rollup (`vite.config.js` or `rollup.config.js`):
export default {
build: {
rollupOptions: {
onwarn(warning, warn) {
if (warning.code === 'UNUSED_EXTERNAL_IMPORT' && warning.exporter?.includes('@supabase/'))
return
warn(warning)
},
},
},
}Nuxt (`nuxt.config.ts`):
export default defineNuxtConfig({
vite: {
build: {
rollupOptions: {
onwarn(warning, warn) {
if (warning.code === 'UNUSED_EXTERNAL_IMPORT' && warning.exporter?.includes('@supabase/'))
return
warn(warning)
},
},
},
},
})Contributing
We welcome contributions! Please see our Contributing Guide for details on how to get started.
For major changes or if you're unsure about something, please open an issue first to discuss your proposed changes.
Building
# From the monorepo root
npx nx build supabase-js
# Or with watch mode for development
npx nx build supabase-js --watchTesting
There's a complete guide on how to set up your environment for running locally the supabase-js integration tests. Please refer to TESTING.md.
Badges
