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

Credentials

Which credential goes where. Management credentials, SQL connection strings, and why they are never interchangeable.

dbSDK works with two different kinds of credentials, and mixing them up is the most common setup mistake. This page states exactly which secret goes into which option, per provider.

The two credential planes

Management plane Query plane
What it authorizes Provider control-plane API calls: create, list, update, delete resources PostgreSQL connections: queries, transactions, batches
Supabase secret Personal access token (sbp_...) The database password inside a postgresql:// connection string
Neon secret Neon API key The role password inside a postgresql:// connection string
dbSDK option supabaseManagement({ accessToken }), neonManagement({ apiKey }) supabase({ connectionString }), neon({ connectionString }), postgres({ connectionString })

A management credential does not authenticate a SQL connection, and a SQL connection string cannot create or delete resources. If you pass a token to the query adapter or a connection string to the management client, the provider will reject you, and dbSDK reports that as an AUTH or PERMISSION error rather than guessing.

Supabase

Supabase issues three different kinds of secrets, and only two of them matter for dbSDK:

  1. Personal access token (sbp_...), generated in the Supabase dashboard’s account settings. This is the management credential. It authorizes the Management API at api.supabase.com, including creating and deleting projects. Specific permissions matter: creating projects requires the organization_projects_create scope on the token, and updating projects requires project_admin_write; a token without them gets the provider’s permission error, normalized as PERMISSION. The token is long-lived and powerful; treat it like root. It is not a database password, and the query adapter will not accept it.
  2. The database password, which lives inside the postgresql:// connection string for a project. This is what the query adapter needs.
  3. Anon and service_role API keys: these belong to Supabase’s Data API (PostgREST), not to the PostgreSQL wire protocol and not to the Management API. dbSDK uses neither; do not pass them to either client.

One timing fact to plan around: a freshly created Supabase project does not come with a usable connection string in hand. The Management API never echoes the database password. Two honest paths:

  • Pass your own password in the create spec; you know it because you chose it.
  • Omit password; the SDK generates a cryptographically random one and returns it exactly once in the result’s secrets (label password). Store it then; it is not retrievable again through the SDK.

Either way, assemble the query connection string from the password and the project’s database host, which the management adapter can retrieve for you (raw.databaseHost(projectRef)) or which you can read from the Supabase dashboard. Full branch credentials are available the same way (raw.branchConfig(...), secrets omitted unless you opt in with includeSecrets: true). The SDK never fabricates a URL from the token.

Neon

  1. Neon API key, created in the Neon console. This is the management credential for the Neon API at console.neon.tech. Keys can be scoped; a key scoped to an organization or project cannot manage resources outside that scope, and the provider answers such requests with its own permission error, normalized as PERMISSION.
  2. Role passwords, which live inside Neon’s postgresql:// connection strings. This is what the query adapter needs.

Neon’s API can hand you a ready connection URI: create responses carry them (shown once), and the adapter’s raw.connectionUri() retrieves one on demand. The URI embeds the role password, so dbSDK redacts the password by default and returns the real URI only when you pass reveal: true. Treat the result as a secret either way.

Handling rules

  • Both credential kinds are server-side secrets. There is no browser build of dbSDK, and neither credential belongs in client-side code.
  • The management credential is more powerful than any single database password: it can create and destroy resources on your account. Scope it as narrowly as the provider allows, rotate it per the provider’s guidance, and never commit it.
  • Secrets that dbSDK returns (Neon connection URIs, generated Supabase passwords) are shown once by the provider or by the SDK and are redacted from every raw payload and error message. They are never logged.
  • Environment variables on the server are the expected storage. dbSDK reads credentials only from the options you pass, never from your filesystem or shell.

Where each one goes, in one snippet

import { createDatabase } from "dbsdk";
import { createManagement } from "dbsdk/management";
import { neon } from "dbsdk/neon";
import { neonManagement } from "dbsdk/management/neon";

// Management plane: Neon API key.
const management = createManagement({
  adapter: neonManagement({ apiKey: process.env.NEON_API_KEY! }),
});

// Query plane: a postgresql:// connection string, not the API key.
const db = createDatabase({
  adapter: neon({ connectionString: process.env.DATABASE_URL! }),
});

The same split applies to Supabase: accessToken for supabaseManagement, the postgresql:// string for supabase. See Management for the full lifecycle.

Was this page helpful?