supabase-client-sdk
v1.2.0
Published
A TypeScript client SDK for the Supabase Management API — manage projects, organizations, databases, auth, edge functions, storage, and more.
Maintainers
Readme
Supabase Client SDK
A TypeScript client SDK for the Supabase Management API — manage projects, organizations, databases, auth, edge functions, storage, and more.
Installation
npm install supabase-client-sdkQuick Start
With a PAT (Personal Access Token)
import { SupabaseClient } from 'supabase-client-sdk';
const client = new SupabaseClient({
accessToken: process.env.SUPABASE_ACCESS_TOKEN!,
});
// List all projects
const projects = await client.projects.list();
console.log(projects);With CLI-style login (like supabase login)
import { SupabaseClient } from 'supabase-client-sdk';
const client = await SupabaseClient.login();
// Opens browser → log in → paste 8-character code → authenticated
// Query the database
const schemas = await client.database.listSchemas('your-project-ref');
const tables = await client.database.listTables('your-project-ref', 'public');
const rows = await client.database.selectRows('your-project-ref', 'public.users', {
limit: 5,
});Authentication
This SDK supports three authentication methods:
1. CLI Login (like supabase login) — No config needed
Interactive browser-based login, identical to how the official Supabase CLI works. No OAuth app registration or client_secret required.
import { SupabaseClient, login } from 'supabase-client-sdk';
// Option A: Static method on SupabaseClient
const client = await SupabaseClient.login();
// Option B: Standalone function (get the token yourself)
const token = await login();
const client = new SupabaseClient({ accessToken: token });How it works:
- The SDK generates an ECDH P-256 key pair for end-to-end encryption
- Opens your browser to
https://supabase.com/dashboard/cli/loginwith a session ID and public key - You log in on the Supabase dashboard → it shows an 8-character verification code
- You paste the code in the terminal
- The SDK polls
GET /platform/cli/login/{session_id}?device_code={code} - The server returns the access token encrypted with AES-256-GCM using the ECDH shared secret
- The SDK decrypts it → you're authenticated
Options:
const token = await login({
apiUrl: 'https://api.supabase.com', // default
dashboardUrl: 'https://supabase.com/dashboard', // default
openBrowser: true, // auto-open browser (default)
tokenName: 'my-workstation', // label shown in your Supabase dashboard
signal: abortController.signal, // cancel the flow
onVerificationRequest: async () => {
// Custom prompt instead of the default stdin input
return await readLine('Enter the 8-character code: ');
},
});2. Personal Access Token (PAT) — Quick & Simple
Generate a PAT from your Supabase dashboard:
const client = new SupabaseClient({
accessToken: process.env.SUPABASE_ACCESS_TOKEN!,
});Best for: CI/CD, server-side scripts, and environments where browser-based flows aren't possible.
3. OAuth 2.1 (Authorization Code + PKCE)
For CLI tools, interactive applications, or any environment where you want a proper OAuth flow with automatic token refresh. Uses Authorization Code Grant with PKCE (RFC 7636), the only grant type supported by the Supabase OAuth server.
⚠️ Prerequisite: You need to create an OAuth App in your Supabase organization settings and add
http://localhost:4123/callbackas a redirect URI. The SDK starts a local callback server on port 4123 to receive the authorization code after you authorize in the browser.
import { SupabaseClient } from 'supabase-client-sdk';
const client = new SupabaseClient({
oauth: {
clientId: 'your-client-id',
clientSecret: 'your-client-secret', // optional for confidential clients
},
});
// On first use, the SDK will:
// 1. Start a local callback server on port 4123
// 2. Open your browser to the Supabase authorization page
// 3. After you authorize, Supabase redirects to http://localhost:4123/callback
// 4. The SDK exchanges the code for an access token
// 5. Automatically refresh tokens when they expire
const projects = await client.projects.list();Creating an OAuth App
- Go to https://supabase.com/dashboard/org/_/apps
- Click Add New App
- Set the redirect URI to:
http://localhost:4123/callback - Save the app and copy the Client ID
- Use it in the SDK:
clientId: 'your-client-id'
Custom Token Storage
By default, tokens are kept in memory only. To persist tokens across sessions (so the user doesn't have to re-authorize every time), provide a token store:
import type { TokenStore, StoredToken } from 'supabase-client-sdk';
import { readFile, writeFile } from 'node:fs/promises';
const fileStore: TokenStore = {
async get(): Promise<StoredToken | null> {
try {
const data = await readFile('./supabase-token.json', 'utf-8');
return JSON.parse(data);
} catch {
return null;
}
},
async set(token: StoredToken): Promise<void> {
await writeFile('./supabase-token.json', JSON.stringify(token, null, 2));
},
async clear(): Promise<void> {
await writeFile('./supabase-token.json', '{}');
},
};
const client = new SupabaseClient({
oauth: {
tokenStore: fileStore,
},
});Custom Authorization Prompt
By default, the SDK prints the authorization URL to stdout. You can override this for headless environments or custom UIs:
const client = new SupabaseClient({
oauth: {
onAuthorizationRequest(authUrl) {
console.log(`Please visit this URL to authorize:`);
console.log(authUrl);
},
},
});Manual Token Management
You can access the underlying OAuth client to manually clear tokens (e.g., on logout):
const oauthClient = client.getOAuthClient();
if (oauthClient) {
await oauthClient.clearTokens();
}API Reference
SupabaseClient
The main client class. Instantiate with an access token and optional configuration.
const client = new SupabaseClient({
accessToken: 'sbp_xxxxx',
baseUrl?: 'https://api.supabase.com', // default
headers?: { 'User-Agent': 'my-app/1.0' },
});Services
All services are accessible as properties of the client instance.
| Property | Service | Description |
|---|---|---|
| .organizations | OrganizationsService | Manage organizations & members |
| .projects | ProjectsService | Manage projects |
| .auth | AuthService | Auth config & user management |
| .functions | FunctionsService | Edge Functions |
| .storage | StorageService | Storage buckets |
| .secrets | SecretsService | Project secrets |
| .database | DatabaseService | DB introspection & SQL |
| .backups | BackupsService | Database backups |
| .sslEnforcement | SslEnforcementService | SSL enforcement |
| .networkRestrictions | NetworkRestrictionsService | Firewall rules |
| .customDomains | CustomDomainsService | Custom domains |
| .vanitySubdomains | VanitySubdomainsService | Vanity subdomains |
| .branches | BranchService | Preview branches |
OrganizationsService
// List all organizations
client.organizations.list()
// Get organization by slug
client.organizations.get('my-org')
// Create organization
client.organizations.create({ name: 'My Org', billing_email: '[email protected]' })
// Update organization
client.organizations.update('my-org', { name: 'New Name' })
// Delete organization
client.organizations.remove('my-org')
// List members
client.organizations.listMembers('my-org')
// Invite member
client.organizations.inviteMember('my-org', { email: '[email protected]', role: 'Developer' })
// Remove member
client.organizations.removeMember('my-org', 'member_id')
// Update member role
client.organizations.updateMemberRole('my-org', 'member_id', 'Administrator')ProjectsService
// List all projects
client.projects.list()
// Get project details
client.projects.get('ref_abc')
// Create a project
client.projects.create({
name: 'my-project',
organization_id: 'org_abc',
region: 'us-east-1',
plan: 'pro',
})
// Update project
client.projects.update('ref_abc', { name: 'new-name' })
// Delete project (irreversible!)
client.projects.remove('ref_abc')
// Pause / Resume
client.projects.pause('ref_abc')
client.projects.resume('ref_abc')
// Health check
client.projects.getHealth('ref_abc')
// Restore from backup
client.projects.restore('ref_abc', { backup_id: 'backup_123' })
// List API keys
client.projects.listApiKeys('ref_abc')AuthService
// Get auth config
client.auth.getConfig('ref_abc')
// Update auth config
client.auth.updateConfig('ref_abc', { disable_signup: true })
// List users
client.auth.listUsers('ref_abc')
// Get user
client.auth.getUser('ref_abc', 'user_id')
// Create user
client.auth.createUser('ref_abc', {
email: '[email protected]',
password: 'securepass',
email_confirm: true,
})
// Update user
client.auth.updateUser('ref_abc', 'user_id', { ban_duration: '24h' })
// Delete user
client.auth.deleteUser('ref_abc', 'user_id')
// Generate magic link / invite
client.auth.generateLink('ref_abc', {
type: 'magiclink',
email: '[email protected]',
})FunctionsService
// List functions
client.functions.list('ref_abc')
// Get function
client.functions.get('ref_abc', 'hello-world')
// Create function
client.functions.create('ref_abc', {
slug: 'hello-world',
name: 'Hello World',
body: 'export default async function(req) { return new Response("Hi!"); }',
})
// Update function
client.functions.update('ref_abc', 'hello-world', { name: 'Updated' })
// Delete function
client.functions.remove('ref_abc', 'hello-world')
// Deploy new version
client.functions.deploy('ref_abc', 'hello-world', {
body: 'export default async function(req) { return new Response("v2!"); }',
})StorageService
// List buckets
client.storage.listBuckets('ref_abc')
// Get bucket
client.storage.getBucket('ref_abc', 'bucket_id')
// Create bucket
client.storage.createBucket('ref_abc', {
name: 'images',
public: true,
file_size_limit: 10485760, // 10 MB
})
// Update bucket
client.storage.updateBucket('ref_abc', 'bucket_id', { public: false })
// Delete bucket
client.storage.deleteBucket('ref_abc', 'bucket_id')
// Empty bucket
client.storage.emptyBucket('ref_abc', 'bucket_id')SecretsService
// List secrets
client.secrets.list('ref_abc')
// Create/update secrets
client.secrets.upsert('ref_abc', [
{ name: 'MY_API_KEY', value: 'secret-value' },
{ name: 'DATABASE_URL', value: 'postgres://...' },
])
// Delete secrets
client.secrets.remove('ref_abc', ['MY_API_KEY', 'DATABASE_URL'])DatabaseService
Run SQL queries, inspect schemas, tables, and columns, and perform CRUD
operations on table rows — all via the Management API's POST /v1/projects/{ref}/database/query endpoint.
The read-only variants use supabase_read_only_user for an extra layer of safety.
// ─── SQL ───────────────────────────────────────────────────────────
// Run any SQL (SELECT, INSERT, UPDATE, DELETE, DDL)
client.database.runSql('ref_abc', 'SELECT * FROM users LIMIT 5')
// Read-only SQL (supabase_read_only_user) — safer for queries
client.database.runSqlReadOnly('ref_abc', 'SELECT count(*) FROM users')
// ─── Database Introspection ────────────────────────────────────────
// List all schemas in the database
client.database.listSchemas('ref_abc')
// → [{ schema_name: 'public' }, { schema_name: 'auth' }, ...]
// List all tables in a schema (defaults to 'public')
client.database.listTables('ref_abc')
client.database.listTables('ref_abc', 'auth')
// → [{ table_name: 'users', table_type: 'BASE TABLE', table_schema: 'public' }, ...]
// List columns in a table (schema-qualified names supported)
client.database.listColumns('ref_abc', 'public.users')
// → [{ column_name: 'id', data_type: 'uuid', is_nullable: 'NO', ordinal_position: 1 }, ...]
// List all database roles/users
client.database.listRoles('ref_abc')
// → [{ rolname: 'postgres', rolsuper: true, rolcanlogin: true }, ...]
// List installed Postgres extensions
client.database.listExtensions('ref_abc')
// → [{ extname: 'pgcrypto', extversion: '1.3', extnamespace: 'public' }, ...]
// ─── Table Row CRUD ────────────────────────────────────────────────
// Select rows with filtering, ordering, pagination
client.database.selectRows('ref_abc', 'public.users', {
columns: ['id', 'email', 'created_at'],
where: { status: 'active' },
orderBy: 'created_at DESC',
limit: 10,
offset: 20,
})
// Insert one or more rows
client.database.insertRows('ref_abc', 'public.users', {
email: '[email protected]',
name: 'Alice',
})
// Insert multiple rows at once
client.database.insertRows('ref_abc', 'public.users', [
{ email: '[email protected]', name: 'Bob' },
{ email: '[email protected]', name: 'Carol' },
])
// Update rows (WHERE clause is REQUIRED for safety)
client.database.updateRows('ref_abc', 'public.users',
{ status: 'inactive' },
{ where: { email: '[email protected]' }, returning: true }
)
// Delete rows (WHERE clause is REQUIRED for safety)
client.database.deleteRows('ref_abc', 'public.users',
{ where: { email: '[email protected]' }, returning: true }
)
// ─── Pooler & Pgsodium ─────────────────────────────────────────────
// Get pooler configuration
client.database.getPoolerConfig('ref_abc')
// Get pgsodium root key
client.database.getPgsodiumConfig('ref_abc')BackupsService
// List backups
client.backups.list('ref_abc')
// Restore from backup
client.backups.restore('ref_abc', { backup_id: 'backup_123' })
// Get backup config
client.backups.getConfig('ref_abc')SslEnforcementService
// Get current SSL enforcement
client.sslEnforcement.get('ref_abc')
// Update SSL enforcement
client.sslEnforcement.update('ref_abc', {
enforce_ssl: true,
database_requires_ssl: true,
})NetworkRestrictionsService
// Get network restrictions
client.networkRestrictions.get('ref_abc')
// Apply IP restrictions
client.networkRestrictions.apply('ref_abc', {
db_allow_cidr: ['0.0.0.0/0'],
allowed_ips: ['203.0.113.0/24'],
})CustomDomainsService
// List custom domains
client.customDomains.list('ref_abc')
// Get custom domain
client.customDomains.get('ref_abc', 'domain_id')
// Activate custom domain
client.customDomains.activate('ref_abc', 'api.example.com')
// Reverify domain
client.customDomains.reverify('ref_abc', 'domain_id')
// Get DNS verification records
client.customDomains.getVerification('ref_abc', 'domain_id')
// Remove custom domain
client.customDomains.remove('ref_abc', 'domain_id')VanitySubdomainsService
// Get current vanity subdomain
client.vanitySubdomains.get('ref_abc')
// Set vanity subdomain
client.vanitySubdomains.set('ref_abc', 'my-app')
// Remove vanity subdomain
client.vanitySubdomains.remove('ref_abc')BranchService
// List branches
client.branches.list('ref_abc')
// Get branch
client.branches.get('ref_abc', 'branch_id')
// Create branch
client.branches.create('ref_abc', { name: 'feature-branch' })
// Update branch
client.branches.update('ref_abc', 'branch_id', { reset_on_push: true })
// Delete branch
client.branches.remove('ref_abc', 'branch_id')
// Reset branch to parent schema
client.branches.reset('ref_abc', 'branch_id')Error Handling
The SDK throws SupabaseApiError for non-2xx responses:
import { SupabaseClient, SupabaseApiError } from 'supabase-client-sdk';
try {
await client.projects.get('invalid-ref');
} catch (error) {
if (error instanceof SupabaseApiError) {
console.error(`API Error [${error.status}]: ${error.message}`);
console.error(`Path: ${error.path}`);
console.error(`Body:`, error.body);
}
}Development
# Install dependencies
npm install
# Build
npm run build
# Tests
npm testLicense
MIT
