npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

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.

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-sdk

Quick 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:

  1. The SDK generates an ECDH P-256 key pair for end-to-end encryption
  2. Opens your browser to https://supabase.com/dashboard/cli/login with a session ID and public key
  3. You log in on the Supabase dashboard → it shows an 8-character verification code
  4. You paste the code in the terminal
  5. The SDK polls GET /platform/cli/login/{session_id}?device_code={code}
  6. The server returns the access token encrypted with AES-256-GCM using the ECDH shared secret
  7. 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/callback as 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

  1. Go to https://supabase.com/dashboard/org/_/apps
  2. Click Add New App
  3. Set the redirect URI to: http://localhost:4123/callback
  4. Save the app and copy the Client ID
  5. 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 test

License

MIT