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

@ankhorage/supabase-db

v0.3.0

Published

Supabase database adapter for Ankhorage contracts with CRUD, schema management, and optional realtime support.

Readme

@ankhorage/supabase-db

Supabase database adapter for Ankhorage data contracts.

@ankhorage/supabase-db implements Supabase-specific persistence behind the provider-neutral database interfaces from @ankhorage/contracts/db. Higher-level packages can use typed CRUD operations, optional realtime subscriptions, and guarded schema helpers without importing Supabase details into UI, runtime rendering, generated apps, or ZORA components.

Install

bun add @ankhorage/supabase-db @ankhorage/contracts

Install @supabase/supabase-js as well when you want to pass a Supabase realtime client:

bun add @supabase/supabase-js

Boundaries

This package owns Supabase Database behavior:

  • adapter creation
  • table select workflows
  • insert, update, and delete workflows
  • filter, order, and pagination mapping
  • provider error normalization
  • optional realtime change subscriptions
  • guarded schema SQL generation and privileged execution hooks

This package does not own:

  • ZORA components or patterns
  • Studio UI
  • runtime manifest interpretation
  • generated routes or layouts
  • CLI generation
  • deployment orchestration
  • auth or storage adapters

Runtime CRUD adapter

Use the runtime adapter with client-safe Supabase credentials. The adapter speaks the canonical DbAdapter shape from @ankhorage/contracts/db.

import { createSupabaseDbAdapter } from '@ankhorage/supabase-db';

const db = createSupabaseDbAdapter({
  url: process.env.SUPABASE_URL ?? '',
  anonKey: process.env.SUPABASE_ANON_KEY ?? '',
});

const posts = await db.select({
  table: 'posts',
  columns: ['id', 'title', 'created_at'],
  filters: [{ field: 'published', operator: 'eq', value: true }],
  sort: [{ field: 'created_at', direction: 'desc' }],
  page: { limit: 20 },
});

if (posts.ok) {
  console.log(posts.data);
}

The runtime adapter uses Supabase PostgREST endpoints and returns normalized DbResult values. Expected provider failures, permission errors, missing tables, and invalid queries are returned as stable adapter errors instead of raw Supabase response objects.

Supported CRUD operations

The adapter implements:

  • select
  • findById
  • insert
  • update
  • delete

update and delete require at least one filter to avoid accidental whole-table mutations.

Capabilities

The adapter exposes the canonical DbAdapterCapabilities contract:

const capabilities = db.capabilities;

console.log(capabilities.transactions); // false
console.log(capabilities.returning); // true
console.log(capabilities.realtime); // true only when realtime is enabled and configured

Realtime

Realtime is optional. It is exposed through the canonical DbRealtimeAdapter contract only when enabled and configured.

import { createClient } from '@supabase/supabase-js';
import { createSupabaseDbAdapter } from '@ankhorage/supabase-db';

const supabase = createClient(process.env.SUPABASE_URL ?? '', process.env.SUPABASE_ANON_KEY ?? '');

const db = createSupabaseDbAdapter({
  url: process.env.SUPABASE_URL ?? '',
  anonKey: process.env.SUPABASE_ANON_KEY ?? '',
  realtime: true,
  realtimeClient: supabase,
});

const subscription = db.realtime?.subscribeToCollection({ table: 'posts' }, (event) => {
  console.log(event.kind, event.record, event.previousRecord);
});

await subscription?.unsubscribe();

Realtime events are normalized to provider-neutral kinds:

  • insert
  • update
  • delete

Supabase projects must have database change replication configured for realtime table events. If realtime is not enabled or no realtime client is provided, CRUD still works and capabilities.realtime is false.

Admin/schema adapter

Schema operations are privileged and separate from runtime CRUD. The admin adapter implements the canonical DbAdminAdapter contract from @ankhorage/contracts/db.

By default, the admin adapter generates SQL only:

import { createSupabaseDbAdminAdapter } from '@ankhorage/supabase-db';

const admin = createSupabaseDbAdminAdapter({
  url: process.env.SUPABASE_URL ?? '',
  serviceRoleKey: process.env.SUPABASE_SERVICE_ROLE_KEY,
});

const plan = admin.generateCreateCollectionSql({
  name: 'posts',
  fields: [
    { name: 'title', type: 'text', required: true },
    { name: 'body', type: 'text' },
    { name: 'created_at', type: 'datetime' },
  ],
});

if (plan.ok) {
  console.log(plan.sql);
}

Direct execution requires all of the following:

  • execute: true
  • a serviceRoleKey
  • an injected executeSql(sql) callback from a privileged environment
const admin = createSupabaseDbAdminAdapter({
  url: process.env.SUPABASE_URL ?? '',
  serviceRoleKey: process.env.SUPABASE_SERVICE_ROLE_KEY,
  execute: true,
  executeSql: async (sql) => {
    // Run SQL through a trusted backend, migration runner, or RPC you control.
    return { ok: true };
  },
});

Do not use service-role credentials in client/runtime code.

The admin adapter is intended for trusted Studio backends, CLI tooling, deployment tooling, or migration workflows. It must not be bundled into generated client screens.

Data binding architecture

A future app flow should look like this:

Studio creates a collection definition
  -> privileged adapter generates or applies schema
Runtime queries rows through DbAdapter
  -> runtime maps row fields to component props
ZORA renders presentational patterns only

For example, a future ZORA PostCard should receive props. It should not import Supabase or this package.

Development

bun install
bun run build
bun run lint:fix
bun run test

Tests are mocked and must not call real Supabase services.