Development
Lakebase Postgres development
This page covers developing against Lakebase Postgres from an AppKit app. For Lakebase itself (projects, branches, autoscaling, connectivity), see the Lakebase docs or the databricks-lakebase agent skill.
AppKit plugin API
The lakebase() plugin provides a standard pg.Pool with automatic OAuth token refresh. Once registered, access it via AppKit.lakebase:
import { createApp, lakebase, server } from "@databricks/appkit";
const AppKit = await createApp({
plugins: [server(), lakebase()],
});
// Standard parameterized query
const { rows } = await AppKit.lakebase.query<{ id: number; name: string }>(
"SELECT id, name FROM app.items WHERE active = $1",
[true],
);
// ORM-ready config (Drizzle, Prisma, TypeORM, etc.)
const ormConfig = AppKit.lakebase.getOrmConfig();
// Returns: { host, port, database, ssl, user, ... }
// pg-compatible config
const pgConfig = AppKit.lakebase.getPgConfig();
// Raw pg.Pool for advanced usage
const pool = AppKit.lakebase.pool;Pool configuration
Override connection pool defaults by passing a pool object:
lakebase({
pool: {
max: 10, // max connections (default: 10)
connectionTimeoutMillis: 5000, // connection timeout ms (default: 10000)
idleTimeoutMillis: 30000, // idle timeout ms (default: 30000)
},
});The max: 10 default applies to the shared service-principal pool. Per-user on-behalf-of pools (created by asUser(req)) default to max: 3.
Caching integration
Lakebase Postgres also backs the AppKit caching plugin when healthy. For the full API, ORM integration, and connection configuration, read the plugin reference.
Auth model
Lakebase Postgres authenticates database connections using OAuth tokens or native Postgres passwords. The method depends on where your app runs.
Deployed apps: When you add it as a resource to a Databricks App, Databricks creates a service principal automatically, grants it a matching Postgres role, and injects connection details as environment variables. AppKit's lakebase() plugin handles OAuth token refresh automatically.
Local development: Your personal Databricks identity connects with an OAuth token generated by databricks postgres generate-database-credential. Tokens expire after one hour, but expiration is enforced only at login. Open connections remain active after the token expires. Run databricks apps deploy at least once before running npm run dev. Local setup explains why order matters and what to do if you hit permission errors.
About authentication covers Postgres password auth, token rotation, and machine-to-machine flows.
Local setup
databricks apps init populates .env with the correct Lakebase Postgres connection values. Run databricks apps deploy before npm run dev. Deploying sets up a managed identity (the app's service principal) that creates the app schema and tables on first startup and owns them. If npm run dev runs first instead, your personal credentials create those objects. The deployed app then can't access them and hits permission denied for schema app.
Local database access
If you created the Lakebase Postgres project, your identity already has the access it needs. After databricks apps deploy runs once, npm run dev works.
For collaborators who need local read/write access, grant them a role on the branch in the Lakebase UI (Roles & Databases). Postgres password auth is an alternative to OAuth: enable password connections, create a password role, then use the password as PGPASSWORD in .env. About authentication has the steps for both.
You can also generate a short-lived credential for use with any PostgreSQL client (DBeaver, pgAdmin, DataGrip, or a language driver):
databricks postgres generate-database-credential \
projects/my-project/branches/production/endpoints/primaryThe AppKit plugin docs: local development cover fine-grained permission alternatives for teams that need schema-scoped access.
Connect with psql
databricks psql opens an interactive PostgreSQL session against a branch endpoint. It requires psql to be installed locally. With no target, it prompts you to pick from the databases you can access.
databricks psql --project my-project| Option | Description |
|---|---|
--autoscaling | Only show Lakebase Autoscaling projects |
--project | Project ID |
--branch | Branch ID (default: auto-select) |
--endpoint | Endpoint ID (default: auto-select) |
--max-retries | Connection retries; 0 to disable (default 3) |
--debug | enable debug logging |
--output, -o | output type: text or json (default text) |
--profile, -p | ~/.databrickscfg profile |
--target, -t | bundle target to use (if applicable) |
Pass extra arguments straight to psql after a -- separator, for example databricks psql --project my-project -- -c "SELECT 1".
Feature branches
Use Lakebase Postgres branches to isolate schema changes and test migrations without affecting production:
databricks postgres create-branch projects/my-project feature-xyz \
--json '{"spec": {"no_expiry": true}}'| Option | Description |
|---|---|
--json | either inline JSON string or @path/to/file.json with request body (default JSON (0 bytes)) |
--no-wait | do not wait to reach DONE state |
--replace-existing | If true, update the branch if it already exists instead of returning an error. |
--timeout | maximum amount of time to reach DONE state |
--debug | enable debug logging |
--output, -o | output type: text or json (default text) |
--profile, -p | ~/.databrickscfg profile |
--target, -t | bundle target to use (if applicable) |
A primary read-write endpoint is created automatically, inheriting the project's default_endpoint_settings. Branches require an expiration policy (ttl, expire_time, or no_expiry: true). Branch expiration details the available policies.
Delete when done:
databricks postgres delete-branch projects/my-project/branches/feature-xyz| Option | Description |
|---|---|
--no-wait | do not wait to reach DONE state |
--purge | If true, permanently delete the branch; if false, soft delete. |
--timeout | maximum amount of time to reach DONE state |
--debug | enable debug logging |
--output, -o | output type: text or json (default text) |
--profile, -p | ~/.databrickscfg profile |
--target, -t | bundle target to use (if applicable) |
Off-platform apps
For apps hosted outside Databricks (AWS, Vercel, Netlify, and others), the platform does not inject connection details or refresh OAuth tokens automatically. Token rotation is the app's responsibility. About Lakebase authentication covers token rotation and machine-to-machine patterns. The Lakebase Off-Platform template includes a complete implementation with environment setup and Drizzle ORM integration.
To provision and connect without a template, create a project, read its endpoint and database, then connect:
databricks postgres create-project <project-id>
databricks postgres list-endpoints projects/<project-id>/branches/production -o json
databricks postgres list-databases projects/<project-id>/branches/production -o json
databricks psql --project <project-id>create-project makes a project with a default production branch, a databricks_postgres database, and a read-write endpoint. If you don't have psql, run databricks postgres generate-database-credential <endpoint-path> and use the returned token as the password (username is your Databricks email) with any PostgreSQL client. See the Lakebase docs or the databricks-lakebase agent skill for the full flow and flags.
The values you need from the list-endpoints and list-databases output:
| Value | JSON path | Used for |
|---|---|---|
| Endpoint host | status.hosts.host | PGHOST |
| Endpoint resource path | name | LAKEBASE_ENDPOINT |
| Database resource path | name (from list-databases) | lakebase.postgres.database |
| PostgreSQL database name | status.postgres_database | PGDATABASE |
Long-running operations
Create, update, and delete commands block until complete by default. Use --no-wait to return immediately and poll status:
databricks postgres create-project my-project \
--json '{"spec": {"display_name": "My Project"}}' \
--no-wait
databricks postgres get-operation projects/my-project/operations/<operation-id>Declarative Automation Bundles
Declarative Automation Bundles (DABs) let you define Lakebase Postgres infrastructure as code in databricks.yml, versioned alongside your application. A bundle specifies postgres_projects, postgres_branches, and postgres_endpoints under resources.
Example databricks.yml with a project, dev branch, and read-only replica
bundle:
name: my-lakebase-app
resources:
postgres_projects:
my_app:
project_id: "my-lakebase-app"
display_name: "My Lakebase Postgres App"
pg_version: 17
history_retention_duration: "172800s"
default_endpoint_settings:
autoscaling_limit_min_cu: 0.5
autoscaling_limit_max_cu: 1.0
suspend_timeout_duration: "300s"
pg_settings:
log_min_duration_statement: "1000"
postgres_branches:
dev_branch:
parent: ${resources.postgres_projects.my_app.id}
branch_id: "dev"
no_expiry: true
is_protected: false
postgres_endpoints:
read_replica:
parent: ${resources.postgres_branches.dev_branch.id}
endpoint_id: "replica"
endpoint_type: "ENDPOINT_TYPE_READ_ONLY"
autoscaling_limit_min_cu: 0.5
autoscaling_limit_max_cu: 0.5Validate and deploy
databricks bundle validate
databricks bundle deploybundle deploy is idempotent. It creates new resources and updates existing ones to match the configuration. Unlike Databricks Jobs or Apps, there is no bundle run step. Lakebase Postgres resources are active once deployed. The Declarative Automation Bundles documentation covers all options, and the databricks-dabs agent skill can author and validate bundles.
Update masks
Update commands require an update mask specifying which fields to modify. The --json payload contains the new values. Only masked fields change.
databricks postgres update-branch \
projects/my-project/branches/production \
spec.is_protected \
--json '{"spec": {"is_protected": true}}'For multiple fields, use a comma-separated update mask (for example, spec.autoscaling_limit_min_cu,spec.autoscaling_limit_max_cu).
Troubleshooting
For Databricks Apps configuration issues (resources in databricks.yml and app.yaml), Add a Lakebase resource to a Databricks app has the resource and environment variable reference. For connection problems including idle wake-up and endpoint format, Troubleshooting in Connect external apps has fixes.
permission denied for schema app(deployed app):npm run devran beforedatabricks apps deploy, so the schema is owned by your personal credentials and the app's service principal can't access it. (PostgreSQL schema ownership is tied to the role that created it and cannot be reassigned by regular users.) If you have data to preserve, export it first (pg_dumpor copy tables to a temporary schema) before dropping. Then drop the schema and redeploy so the SP recreates it on startup:databricks psql --project <project-id> -- -c "DROP SCHEMA IF EXISTS app CASCADE;"thendatabricks apps deploy.permission denied for schema app(local dev, collaborator): Only the Lakebase project creator getsdatabricks_superuseraccess automatically. To grant a teammate local access, the creator adds a role for their identity on the branch (Roles & Databases in the Lakebase UI), or sets up Postgres password auth. See About authentication for the steps.Unknown field path in update_mask: 'spec.suspend_timeout_duration': Usespec.suspensionas the update mask for all endpoint-level suspension changes withupdate-endpoint. To disable scale to zero, pass{"spec": {"no_suspension": true}}. To change the timeout, pass{"spec": {"suspend_timeout_duration": "300s"}}. Settingno_suspension: falseis not supported.- Connection refused after period of inactivity: Lakebase Autoscaling scales to zero when idle. The first connection after inactivity triggers a wake-up and may be briefly delayed. If your connection library doesn't retry automatically, add a short retry loop.
AppKit docs
Access the AppKit API reference, component docs, and plugin docs from the terminal:
npx @databricks/appkit docs # browse the documentation index
npx @databricks/appkit docs "lakebase" # view Lakebase Postgres plugin docsOr view the AppKit Lakebase Postgres plugin reference on this site.
Where to next
Templates cover common Lakebase Postgres patterns. Browse them to find a starting point, or copy one into your coding agent to scaffold a working app.