Skip to content
dbSDK
Esc
↑↓navigate↵open⌘Jpreview
On this page

Getting started

Build dbSDK from a local clone, pick an adapter, and run your first parameterized query.

dbSDK is not on npm yet. This guide installs it from the GitHub repository instead: clone, build the package, and point your app at the build output. Everything after that is the same on every adapter.

1. Build from source

git clone https://github.com/pkyanam/dbSDK.git
cd dbSDK
pnpm install
pnpm --filter dbsdk build

The build emits packages/dbsdk/dist with the entrypoints you will import: dbsdk (core), plus the adapter subpaths dbsdk/postgres, dbsdk/supabase, dbsdk/neon, and dbsdk/testing.

Then bring the package into your app in one of two ways.

Link the built package (fastest for local development):

# from your app directory
npm install /absolute/path/to/dbSDK/packages/dbsdk

Or pack a tarball:

cd packages/dbsdk
npm pack                # produces dbsdk-0.1.0.tgz
cd -
npm install ./packages/dbsdk/dbsdk-0.1.0.tgz   # from your app directory

Driver requirements. The two database drivers are optional peer dependencies, installed only where you use them:

Adapter subpath Driver to install Used for
dbsdk/postgres pg any PostgreSQL endpoint over TCP
dbsdk/supabase pg Supabase Postgres connections
dbsdk/neon @neondatabase/serverless Neon HTTP, WebSocket, and TCP transaction transports

dbsdk itself and dbsdk/testing pull in no database driver, so bundling the core does not bundle a driver you do not use.

2. Pick an adapter and connect

Each adapter wraps the service you already have. You need one connection string from that service’s dashboard; nothing is provisioned for you.

import { createDatabase } from "dbsdk";
import { neon } from "dbsdk/neon";

const db = createDatabase({
  adapter: neon({
    connectionString: process.env.DATABASE_URL!,
    transport: "http", // the default
  }),
});

The same client shape works for every adapter; only the adapter import and its options change. See the adapter pages for the exact options:

  • Postgres: any PostgreSQL endpoint over TCP.
  • Supabase: explicit connectionMode (direct, session, transaction).
  • Neon: transport (http, websocket) and an optional transactionTransport.

Keep connection strings in environment variables on the server. dbSDK never logs them and never puts them in error messages.

3. Run a query

Interpolated values become positional bind parameters. They can never become identifiers or fragments of SQL text.

const { rows, rowCount, command } = await db.sql`
  select id, email from users where id = ${userId}
`;
// rows: Record<string, unknown>[] unless you annotate the result, e.g.:
// const typed: QueryResult<{ id: string; email: string }> = await db.sql`...`
// rowCount: number | null
// command: "SELECT" (the PostgreSQL command tag, when reported)

Every adapter returns that same envelope, on every transport. A write looks like this:

const result = await db.sql`
  insert into users (email) values (${"ada@example.com"})
  returning id
`;
result.rowCount; // 1

Dynamic identifiers are the one thing interpolation must not do, so they have their own validated escape hatch:

import { sql } from "dbsdk";

await db.query(sql`select * from ${sql.identifier(tableName)} limit 10`);

4. Close the connection

close() is idempotent, and every operation after it fails before dispatch. For serverless functions, prefer explicit disposal so the pool shuts down before the invocation returns. The disposal protocol is available in two ways depending on your Node version:

// Node 24+: await using is supported syntax
await using db = createDatabase({ adapter: neon({ connectionString }) });
// closed automatically at the end of the scope
// Node 22 (engines require >=22.12): explicit close
const db = createDatabase({ adapter: neon({ connectionString }) });
try {
  // ... queries
} finally {
  await db.close();
}
// or: await db[Symbol.asyncDispose]() at the end of the scope

5. Next steps

Was this page helpful?