@dbx-tools/appkit-config
v0.1.36
Published
Auto-configuration helpers for AppKit apps. The headline export is `createApp`: a drop-in replacement for `@databricks/appkit`'s `createApp` that resolves and applies whatever environment each enabled capability needs, then delegates to the real `createAp
Readme
@dbx-tools/appkit-config
Auto-configuration helpers for AppKit apps. The headline export is
createApp: a drop-in replacement for @databricks/appkit's createApp
that resolves and applies whatever environment each enabled capability
needs, then delegates to the real createApp with the exact same
arguments (and the same per-plugin export inference).
import { createApp } from "@dbx-tools/appkit-config";
import { lakebase, server } from "@databricks/appkit";
// Because `lakebase()` is present, this resolves the Lakebase env vars
// first, then hands the untouched config to AppKit's createApp.
await createApp({ plugins: [server(), lakebase()] });Auto-config runs BEFORE delegating (so plugins see a populated
process.env during their synchronous setup()) and every step is
self-gating, so apps pay nothing for capabilities they don't use:
| Capability | Trigger | Effect |
| --- | --- | --- |
| Lakebase Postgres (autopg) | a lakebase plugin in config.plugins | resolves + writes PG* / LAKEBASE_* env |
The package is intentionally broader than Postgres: new capability
auto-config slots into createApp behind its own plugin/env signal
without changing the call site.
Standalone autopg()
autopg() fills in every Lakebase Postgres env var the AppKit lakebase
plugin needs from whatever fragments your deployment actually carries.
createApp runs it for you; call it directly only when you want the
resolution without the wrapper:
import { autopg } from "@dbx-tools/appkit-config";
import { createApp, lakebase, server } from "@databricks/appkit";
await autopg();
await createApp({ plugins: [lakebase(), server()] });Why a top-level helper instead of an AppKit plugin
AppKit's static phase field orders plugin setup() invocation, not
async completion. lakebase.setup() synchronously throws on a missing
PGHOST after its first await, so a sibling plugin that performs REST
discovery during setup() races and loses every time. Resolving the env
before createApp(...) delegates sidesteps the race - by the time any
plugin runs, process.env is fully populated.
What it accepts
autopg() looks at, in priority order:
- Explicit
autopg({ project, branch, endpoint, database, host, port, sslMode, autoCreate }) - Env vars - the same ones
lakebasealready reads:LAKEBASE_PROJECT,LAKEBASE_BRANCH,LAKEBASE_ENDPOINTPGHOST,PGDATABASE,PGPORT,PGSSLMODE
- Whatever the address parser can recover from
LAKEBASE_ENDPOINT/config.endpoint
The address input is permissive - any of these work:
# Canonical resource path
LAKEBASE_ENDPOINT="projects/dbx-tools/branches/main/endpoints/primary"
# Full Postgres URI (auth, host, db, sslmode all extracted)
LAKEBASE_ENDPOINT="postgresql://me%[email protected]/databricks_postgres?sslmode=require"
# Bare Lakebase hostname (resolver reverse-looks-up the project)
LAKEBASE_ENDPOINT="ep-steep-forest-e199v43w.database.eastus2.azuredatabricks.net"
# Bare project id (resolver picks the default branch + endpoint + db)
LAKEBASE_ENDPOINT="dbx-tools"Three resolution modes
After parsing, the resolver fills gaps in this order:
- Reverse-lookup - given just a host, scan
projects->branches->endpointsfor a matchingstatus.hosts.hostand recover the owning resource path. - Pick default - given a
project(and optionally abranch), prefer the server-marked default child (status.default,ENDPOINT_TYPE_READ_WRITE,databricks_postgres) and fall back to "the only one" when a listing returns a single result. - Auto-create - when no projects exist at all, create one whose id
defaults to a slugified
projectUtils.name()(override withautoCreate: "my-id"or disable withautoCreate: false). The create call is idempotent: anALREADY_EXISTSresponse from a concurrent boot is treated as success. Then poll the default endpoint untilcurrent_stateisREADYorIDLE.
Options
await autopg({
// Skip writing process.env (just inspect the returned record).
exportEnv: false,
// Pin individual fields - any of these short-circuit the resolver.
project: "dbx-tools",
branch: "main",
endpoint: "projects/dbx-tools/branches/main/endpoints/primary",
database: "databricks_postgres",
// Auto-create behavior.
autoCreate: false, // throw if no project exists
// autoCreate: "my-custom-id", // create with this id
});autopg() returns a Resolved record (project, branch, endpoint,
database, host, port, sslMode). When exportEnv: true (the
default) it also writes the same values to process.env, only filling
gaps - existing values are preserved.
Address parser
The address parser is exported as well if you want it without the resolver wrapper:
import { parseAddress } from "@dbx-tools/appkit-config";
parseAddress("postgresql://[email protected]/dbpg");
// { user, host, database, port?, sslMode?, project?, branch?, endpointId? }Required permissions
The Databricks user / SP behind getWorkspaceClient() needs
postgres.projects.{list,create}, postgres.branches.list,
postgres.endpoints.{list,get}, and postgres.databases.list on the
account.
License
Apache-2.0
