Provision
Create a project or database through your provider's own control plane with one verb: create, then waituntil it is ready. Your credentials stay yours.
Create, manage, and query databases with one SDK.
Supported and planned brands. The compatibility table has the per-capability status.
Provision with the management layer, connect through the adapter, and query with one client. The imports and options change per provider; the verbs do not.
import { createDatabase } from "dbsdk";
import { supabase } from "dbsdk/supabase";
import { createManagement } from "dbsdk/management";
import { supabaseManagement } from "dbsdk/management/supabase";
const management = createManagement({
adapter: supabaseManagement({
accessToken: process.env.SUPABASE_ACCESS_TOKEN!, // sbp_... personal access token
}),
});
// 1. Provision: create the project, then wait until it is active.
const result = await management.create({
kind: "project",
name: "my-app",
organizationId: "my-org-slug", // from management.organizations()
});
const project = await management.wait(result, { timeoutMs: 120_000 });
// 2. Connect to the project you just created: the creation-time password
// (shown once, in result.secrets) and the real host from the API.
const password = result.secrets.find((s) => s.label === "password")!.value;
const host = await management.raw.databaseHost(project.id);
const db = createDatabase({
adapter: supabase({
connectionString: `postgresql://postgres:${encodeURIComponent(password)}@${host}:5432/postgres`,
connectionMode: "direct",
}),
});
// 3. Query: one bound parameter, works on a fresh database.
try {
const value = "hello from dbSDK";
const { rows } = await db.sql`select ${value}::text as value`;
// rows[0].value === "hello from dbSDK"
} finally {
await db.close();
}import { createDatabase } from "dbsdk";
import { neon } from "dbsdk/neon";
import { createManagement } from "dbsdk/management";
import { neonManagement } from "dbsdk/management/neon";
const management = createManagement({
adapter: neonManagement({ apiKey: process.env.NEON_API_KEY! }),
});
// 1. Provision: create the project, then wait until every operation
// finishes. wait() issues GET requests only; it never resubmits.
const result = await management.create({ kind: "project", name: "my-app" });
const project = await management.wait(result, { timeoutMs: 120_000 });
// 2. Connect to that project: an official connection URI, redacted by
// default and revealed only with the explicit reveal: true opt-in.
const uri = await management.raw.connectionUri({
projectId: project.id,
databaseName: "neondb",
roleName: "neondb_owner",
reveal: true,
});
const db = createDatabase({
adapter: neon({ connectionString: uri, transport: "http" }),
});
// 3. Query: one bound parameter, works on a fresh database.
try {
const value = "hello from dbSDK";
const { rows } = await db.sql`select ${value}::text as value`;
// rows[0].value === "hello from dbSDK"
} finally {
await db.close();
}import { createDatabase } from "dbsdk";
import { planetscale } from "dbsdk/planetscale";
import { createManagement } from "dbsdk/management";
import { planetscaleManagement } from "dbsdk/management/planetscale";
const management = createManagement({
adapter: planetscaleManagement({
tokenId: process.env.PLANETSCALE_TOKEN_ID!,
tokenSecret: process.env.PLANETSCALE_TOKEN_SECRET!,
organization: "my-org-slug", // required owner for all CRUD
}),
});
// 1. Provision: cluster_size is required by the official API. Every
// mutation is engine-gated: a non-PostgreSQL parent is refused
// before anything is sent.
const result = await management.create({
kind: "project",
name: "my-database",
providerOptions: { cluster_size: "PS-10-GP" },
});
const database = await management.wait(result);
// 2. Credentials: role passwords are one-time secrets. Create a role on
// the database's default branch ("main"), then read the real host,
// port, username, and database name back through connection().
const scope = { projectId: database.id, branchId: "main" };
const roleResult = await management.create({ kind: "role", name: "app", scope });
const role = await management.wait(roleResult);
const details = await management.connection({ kind: "role", id: role.id, scope });
const password = roleResult.secrets[0]!.value; // shown once, never again
const db = createDatabase({
adapter: planetscale({
connectionString: `postgresql://${encodeURIComponent(details.role!)}:${encodeURIComponent(password)}@${details.host}:${details.port}/${encodeURIComponent(details.database!)}`,
connectionMode: "direct",
}),
});
// 3. Query: one bound parameter, works on a fresh database.
try {
const value = "hello from dbSDK";
const { rows } = await db.sql`select ${value}::text as value`;
// rows[0].value === "hello from dbSDK"
} finally {
await db.close();
}import { createDatabase } from "dbsdk";
import { postgres } from "dbsdk/postgres";
// Connect an existing PostgreSQL endpoint: no provisioning step, the
// same query client the managed providers hand you.
const db = createDatabase({
adapter: postgres({ connectionString: process.env.DATABASE_URL! }),
});
try {
const value = "hello from dbSDK";
const { rows } = await db.sql`select ${value}::text as value`;
// rows[0].value === "hello from dbSDK"
} finally {
await db.close();
}Real factories, real option names, in this order:dbsdk, dbsdk/management, and one adapter entry per provider. The full walkthrough is ingetting started andmanagement. For typed schema queries on the same dbSDK-owned connections, the optional Drizzle bridge is covered in drizzle.
Create a project or database through your provider's own control plane with one verb: create, then waituntil it is ready. Your credentials stay yours.
One verb set over every provider's resources:list, get, update,delete, lifecycle actions, and credential rotation. Refusals are explicit, never silent.
One typed SQL client: parameterized queries, transactions, and atomic batches. The same result envelope on every adapter, on every transport.
One SDK across the major database services. Here is exactly where each provider stands.
| Provider | Create | Manage | Query |
|---|---|---|---|
| AWS RDS | Planned | Planned | Planned |
| AWS Aurora | Planned | Planned | Planned |
| Google Cloud SQL | Planned | Planned | Planned |
| Google AlloyDB | Planned | Planned | Planned |
| Supabase | Supported | Supported | Supported |
| Neon | Supported | Supported | Supported |
| PlanetScale Postgres | Supported | Supported | Supported |
| PlanetScale Vitess | Planned | Planned | Planned |
| Heroku | Planned | Planned | Planned |
| Convex | Planned | Planned | Planned |
| PostgreSQL | Not applicable | Not applicable | Supported |
Supported Planned Not applicable
PlanetScale Postgres and Vitess have separate adapters. Seeprovider capabilities for the per-capability detail.
dbSDK is installed from source today. Clone the repository, add your provider credentials, and follow the guide. The SDK test suite covers every release.
Every page of this site is also available as Markdown and structured data, so humans and coding agents read the same source of truth.
llms.txtindex of every page and agent resourceany page + .mdstart with this page as Markdownopenapi.jsonthe JSON docs APIMCP setupconnect an MCP client to the docs