AZMARA PLATFORM
Packages

@azmr/db-core-interface

The DbAdapter contract shared by @azmr/db and @azmr/db-supabase — types and DbAdapterError, with no dependencies.

@azmr/db-core-interface is the storage-agnostic persistence contract: the DbAdapter interface, its filter and schema types, and the DbAdapterError class. It has no dependencies, so it can be installed on its own, in any runtime, without better-sqlite3 or Supabase.

Both adapters implement it:

PackageBackend
@azmr/dbLocal SQLite (SQLiteAdapter)
@azmr/db-supabaseSupabase / PostgREST (SupabaseAdapter)

Installation

pnpm add @azmr/db-core-interface

You normally do not install it directly: @azmr/db and @azmr/db-supabase depend on it. Install it yourself when you write against DbAdapter without choosing a backend, or when you build a new adapter (Postgres, D1, in-memory).

The DbAdapter interface

Prop

Type

Deliberately left out: rawSelect (arbitrary SQL, a SQLite-only escape hatch that PostgREST cannot implement) and the path getter. Code that needs those depends on SQLiteAdapter directly.

Filters

Conditions are implicitly AND-combined. There is no OR support yet.

Prop

Type

Errors

Adapters throw DbAdapterError when an operation fails. It carries the optional code, details, hint and cause the backend reported.

import { DbAdapterError } from "@azmr/db-core-interface";

try {
  await db.insert("customers", { id: 1 });
} catch (error) {
  if (error instanceof DbAdapterError) {
    console.error(error.code, error.hint);
  }
}

DbAdapterError is a class, so instanceof only works if your app ends up with one copy of this package. @azmr/db and @azmr/db-supabase re-use the same class, and @azmr/db/interface re-exports it, so mixing those imports is safe. If a lock file ever holds two versions of @azmr/db-core-interface, deduplicate them.

Writing against the interface

Code that takes a DbAdapter runs unchanged against either backend:

import type { DbAdapter, Filter } from "@azmr/db-core-interface";

const active: Filter = [{ column: "active", operator: "eq", value: 1 }];

export async function countActive(db: DbAdapter, table: string): Promise<number> {
  return (await db.findWhere(table, active)).length;
}

When you build your own adapter, implement every DbAdapter method and throw DbAdapterError (never a backend-specific error), so callers handle failures the same way against every backend.

Requirements

  • Node.js ≥ 18
  • TypeScript ≥ 5 (types included)
  • ESM only

On this page