@dbx-tools/appkit

中文文档
0.9.206发布于 2026/11/11Apache-2.0TypeScriptES Module构建来源已验证近 30 天下载 1.3万

Node-side helpers for Databricks AppKit applications

原始使用文档
文档目录14 节
  1. Quick start
  2. Use with native AppKit
  3. Auto-configuration behavior
  4. Configuration
  5. Browser debug capture
  6. Filesystem cache plugin
  7. Resolve local and bundle config
  8. Parse Lakebase addresses
  9. Use AppKit execution context safely
  10. Adapt Databricks SDK cancellation
  11. Look up sibling plugins
  12. Provision Lakebase access
  13. Choose the request identity
  14. Modules

@dbx-tools/appkit

Configure Lakebase before Databricks AppKit plugin startup and use AppKit request context, typed plugin lookup, and Databricks SDK cancellation from backend code.

Quick start

Use the wrapper when Lakebase settings must be resolved before plugin setup:

import { lakebase, server } from "@databricks/appkit";
import { appkit } from "@dbx-tools/appkit";

await appkit.createApp({
  plugins: [server(), lakebase()],
});

Use these helpers for:

  • Auto-configuration before AppKit setup, especially for Lakebase/Postgres env values that AppKit plugins read during initialization.
  • Runtime-safe context access for code that may run inside an AppKit request, from a CLI, or from a background script.
  • Typed plugin lookup helpers for AppKit plugins that depend on exports from sibling plugins.
  • Lakebase resolution through the shared @dbx-tools/core configuration path, including local env, bundle, and app.yaml sources.
  • SDK cancellation bridging from web AbortSignal values into Databricks SDK Context values.
  • Lakebase schema and table provisioning for deployments where the app identity must be granted access before plugin initialization.
  • AppKit-global filesystem caching with user-isolated bounded L1 state, cross-user invalidation, and stable framework filesystem sources.
  • An interceptor context on createApp (interceptor?: Interceptor | Interceptor[]) that hands add-ons the computed env, AppKit lifecycle hooks (onLifecycle, using AppKit's own setup:complete / server:ready / shutdown vocabulary). @dbx-tools/tunnel consumes this surface for auxiliary startup and shutdown.

Use with native AppKit

Use native AppKit directly when your app can read its required env vars before createApp() and does not need extra setup around plugin exports or config sources.

Use this package when the friction is around bootstrapping and reuse:

  • AppKit plugins read Lakebase/Postgres env during initialization. This package resolves and applies those values before setup.
  • AppKit exposes request context inside AppKit handlers. These helpers make code safe to call from scripts, tests, and background jobs too.
  • AppKit plugin instances are generic. The lookup helpers keep sibling-plugin access typed and return clear errors.
  • Local CLI flags, bundle validation output, and app.yaml values use the same @dbx-tools/core configuration path.
  • AppKit's asUser(req) throws outside NODE_ENV=development when a request carries no on-behalf-of-user (OBO) token. identity makes falling back to the service principal a configured, per-request decision instead of a NODE_ENV side effect.

Choose the Lakebase path by caller:

  • Use native AppKit lakebase() when a Databricks App already has complete runtime connection configuration.
  • Use this package's appkit.createApp() when Lakebase environment values or grants must be resolved before AppKit plugins initialize.
  • Use @dbx-tools/lakebase for profile-aware discovery and short-lived credentials in standalone Node or Bun code.
  • Use dbx lakebase-proxy when an external PostgreSQL client needs a loopback connection.

Auto-configuration behavior

appkit.createApp is a drop-in wrapper around AppKit createApp with the same config and the same typed plugin-export map. It runs appkit.autoConfigure() first so enabled capabilities can populate environment variables before plugin setup runs.

import { lakebase, server } from "@databricks/appkit";
import { appkit } from "@dbx-tools/appkit";

await appkit.createApp({
  plugins: [server(), lakebase()],
});

When native lakebase() or beta database() is present, auto-config resolves Lakebase Postgres connection settings and fills missing PG* / LAKEBASE_* variables. That avoids a startup race where a plugin reads env before another async setup step can discover it. A database-only app resolves env without implicitly broadening Lakebase grants. AppKit's database plugin owns its own pool.

Auto-configuration is conservative: existing env vars win unless a caller passes explicit options, and local-only discovery is skipped inside a Databricks App environment. This makes the same entrypoint usable in local development, Databricks Asset Bundle validation, and deployed Apps.

During local startup, a missing DATABRICKS_CONFIG_PROFILE is populated from @dbx-tools/auth's default-profile resolution before bundle validation runs. Workspace API calls and Lakebase pool credentials use the same auth lifecycle, so an expired CLI refresh token can invoke databricks auth login instead of falling through to the SDK's unrelated default-auth chain. Deployed Apps retain native AppKit authentication and do not receive a profile.

Boot-time resolution runs as the service principal before any plugin exists, so it sits outside AppKit's interceptor chain. It carries its own timeout, and appkit.autoConfigure() accepts an AbortSignal when a caller wants to cancel it earlier.

Use the lower-level functions when you need to inspect or customize the result:

import { lakebaseResolver } from "@dbx-tools/appkit";

const resolved = await lakebaseResolver.resolveLakebaseConnection({
  endpoint: process.env.LAKEBASE_ENDPOINT,
  autoCreate: false,
});

lakebaseResolver.applyLakebaseToEnv(resolved);

applyLakebaseEnv() does both steps and applies the complete set a Postgres pool needs, including PGUSER. Auto-configuration calls the same function, so a caller outside createApp gets the same env rather than a hand-rolled subset:

const { resolved, user } = await lakebaseResolver.applyLakebaseEnv({ autoCreate: false });

Reach for it when something other than the lakebase() plugin needs a working pool. AppKit's persistent cache is the common case: it builds its own pool from env, and createLakebasePool() throws unless LAKEBASE_ENDPOINT, PGHOST, PGDATABASE, and a username are all present. A Databricks App postgres resource binding supplies only the first. If one is missing, the cache degrades silently to in-memory. @dbx-tools/tunnel's OTP gate calls this for exactly that reason.

Configuration

appkit.createApp() accepts everything AppKit's createApp does, plus:

OptionTypeDefaultDescription
autoConfigure"provision" | "env" | false"provision"What to run before AppKit boots. "provision" resolves the Lakebase connection into process.env and grants the built-in AppKit and Mastra schemas. "env" resolves the connection only. false skips auto-configuration. Omit it to gate resolution on native lakebase or database. Implicit database-only resolution does not grant schemas.
lakebaseProvisionTargetsreadonly string[][]Additional existing Lakebase schemas or schema.table targets. These are additive to the built-in targets and the CSV DBX_TOOLS_APPKIT_LAKEBASE_PROVISION_TARGETS setting.
debug"auto" | boolean"auto"Registers the browser debug-capture plugin. Auto mode enables it only when NODE_ENV=development; true and false force the choice.
interceptorInterceptor | Interceptor[]noneOne or many callbacks handed an InterceptorContext after auto-config computes the env and before AppKit boots. The context carries the resolved env and AppKit lifecycle registration through onLifecycle.

Set autoConfigure explicitly on an app that registers no lakebase() plugin but still wants Lakebase-backed cache persistence. The injected AppKit storage is memory-first: warm hits avoid persistent reads and last_accessed writes, while mutations are coalesced and written behind without blocking the request. When a Lakebase pool is available it is the persistent level. Otherwise the cache uses the portable project-scoped disk level from @dbx-tools/cache. Use "env" inside a deployed app because the app service principal cannot grant access to the cache schema anyway.

createApp() installs this storage by default when AppKit caching is enabled and the caller did not provide cache.storage. A caller-provided storage is preserved. Native AppKit composition can create the same adapter directly:

import { cacheStorage } from "@dbx-tools/appkit";

const storage = await cacheStorage.createAppKitCacheStorage();

AppKit closes injected storage during shutdown. Native composition should call close() when it owns the storage directly. That drains tracked write-behind work before disconnecting the persistent levels.

Browser debug capture

Development createApp() calls automatically register a hidden browser capture route. @dbx-tools/ui/react exposes BrowserDebugCaptureButton, which records bounded browser, viewport, focus, input, pointer, touch, scroll, and resize diagnostics without recording input values or printable key content. Captures are JSONL files under .tmp/debug-captures in development.

Register the plugin explicitly when native AppKit composition or a custom capture directory is required:

import { debugCapture } from "@dbx-tools/appkit/debug";

debugCapture({ directory: ".tmp/browser-captures" });

Filesystem cache plugin

Register filesCache() when provider-backed filesystems should share one AppKit-lifecycle cache:

import { filesCache } from "@dbx-tools/appkit/files-cache";
import { server } from "@databricks/appkit";
import { appkit } from "@dbx-tools/appkit";

await appkit.createApp({
  plugins: [server(), filesCache()],
});

The plugin exposes forScope({ host, workspaceId?, userKey, ttlMs? }), which returns the FileSystemCache consumed by @dbx-tools/shared-fs fs.cache(). Values are isolated by host, optional workspace id, and attributed user. A batched invalidation clears matching keys across active user scopes without sharing authorization-sensitive values.

forFileSystem(scope, { paths, key? }, load) retains one framework filesystem source for the same user and mounted paths. key separates framework instances that use different policies over the same paths. This gives frameworks such as Mastra a stable source identity across page refreshes and turns without introducing a second parsed catalogue cache.

Values and retained filesystem sources use bounded process-local Cacheable stores. Nothing is written to Lakebase or another persistent level. App restarts and replica changes begin with a cold filesystem cache.

This cache is intentionally separate from the application cache adapter. FilesCachePlugin remains the single app-lifetime owner for local-only filesystem values rather than creating one cache per workspace or persisting authorization-sensitive filesystem state.

lakebaseResolver.resolveLakebaseConnection() accepts:

OptionTypeDefaultDescription
endpointstringLAKEBASE_ENDPOINTAny address pgaddress.parseAddress() understands: resource path, Postgres URI, hostname, or bare project id.
projectstringdiscoveredLakebase project id. Resolved from the workspace when unset.
branchstringproject defaultBranch id within the project.
databasestringPGDATABASE, else databricks_postgresPostgres database name.
hoststringPGHOST, else the endpoint's hostPostgres hostname.
portnumberPGPORT, else 5432Postgres port.
sslMode"require" | "disable" | "prefer"PGSSLMODE, else requirePostgres TLS mode.
autoCreatestring | falseslug of the package nameProject id to create when the workspace has none. false fails instead of creating.

Environment variables read during resolution:

VariableDescription
LAKEBASE_ENDPOINTEndpoint resource path, Postgres URI, hostname, or project id.
PGHOSTPostgres hostname. Skips the endpoint lookup when set with PGDATABASE and LAKEBASE_ENDPOINT.
PGDATABASEPostgres database name.
PGPORTPostgres port. A value outside 1-65535 fails with a ValidationError.
PGSSLMODErequire, disable, or prefer. Any other value fails with a ValidationError.
PGUSERConnecting role. Filled from the workspace identity when unset.

Resolved values are written back to process.env by lakebaseResolver.applyLakebaseToEnv(), which never overwrites a variable that is already set. That function covers everything except PGUSER, which needs an await. lakebaseResolver.applyLakebaseEnv() resolves and applies the full set.

Resolve local and bundle config

Configuration is owned by @dbx-tools/core, not re-exported by this package. Its default precedence is constant config, process env, .env, bundle config.env, then app.yaml / app.yml.

import { configUtils } from "@dbx-tools/core";

const warehouseId = configUtils.resolveValue("DATABRICKS_WAREHOUSE_ID", {
  data: { DATABRICKS_WAREHOUSE_ID: flags.warehouse },
});

Use this in CLIs and setup scripts that should behave the same locally and in a Databricks App deployment. configUtils.bundleFile() exposes validated bundle output, while configUtils.appFile() exposes parsed App YAML. Both cache successful, missing, and invalid results for the process lifetime.

Parse Lakebase addresses

pgaddress.parseAddress() accepts resource paths, Postgres URLs, bare Lakebase hosts, and partial inputs. It gives the resolver a common shape without requiring users to remember one canonical format.

import { pgaddress } from "@dbx-tools/appkit";

pgaddress.parseAddress(
  "postgresql://[email protected]/databricks_postgres?sslmode=require",
);

pgaddress.parseResourcePath() is useful when you specifically expect a projects/<id>/branches/<id>/endpoints/<id> value.

Use AppKit execution context safely

appkit.tryGetExecutionContext() returns the active AppKit request context when code is running under AppKit, and undefined elsewhere. That lets libraries preserve OBO auth in apps while still working from scripts.

import { appkit } from "@dbx-tools/appkit";
import { createWorkspaceClient } from "@databricks/appkit";

const client = appkit.tryGetExecutionContext()?.client ?? createWorkspaceClient();

appkit.ensureInitialized() lazily initializes AppKit runtime state before context lookup in code paths that may run early.

Adapt Databricks SDK cancellation

Databricks SDK calls accept a Context. Many app and web APIs use AbortSignal. databricks.toContext() bridges the two.

import { databricks } from "@dbx-tools/appkit";

await client.apiClient.request(
  {
    path: "/api/2.0/serving-endpoints",
    method: "GET",
    headers: new Headers(),
    raw: false,
  },
  databricks.toContext(request.signal),
);

Look up sibling plugins

AppKit's plugin map is intentionally generic. pluginRegistry.data(), pluginRegistry.instance(), and pluginRegistry.require() keep lookups typed and produce better errors when a required plugin is missing.

import { lakebase } from "@databricks/appkit";
import { pluginRegistry } from "@dbx-tools/appkit";

const lake = pluginRegistry.instance(this.context, lakebase);
const pool = lake?.exports().pool;

const required = pluginRegistry.require(this.context, lakebase, "my-plugin").exports();

Use this in AppKit plugins that depend on sibling plugin exports but should not hard-code registered names or casts at every call site. A missing required plugin throws AppKit's ConfigurationError, naming both the caller and the plugin to register.

Provision Lakebase access

createApp({ autoConfigure: "provision" }) includes the existing appkit and mastra_instance schemas. Add application schemas or individual tables with lakebaseProvisionTargets, or set the CSV DBX_TOOLS_APPKIT_LAKEBASE_PROVISION_TARGETS variable. Exact duplicates are removed.

Schema-wide grants run first in their own transactions. When one succeeds, table targets under that schema are skipped. When it fails because the local identity can modify a table but not the whole schema, the failure is logged and the explicit table targets are attempted independently. Missing schemas and tables are debug-logged and skipped.

await appkit.createApp({
  autoConfigure: "provision",
  lakebaseProvisionTargets: ["reporting", "operations.events"],
  plugins: [lakebase()],
});

provision.provisionLakebaseAccess() exposes the same grant runner for callers that already resolved Lakebase connection environment values.

import { provision } from "@dbx-tools/appkit";

await provision.provisionLakebaseAccess("[email protected]", [
  "appkit",
  "mastra_instance",
  "reporting",
  "operations.events",
]);

Pass a third argument to report progress on your own logger. Provisioning is skipped inside a Databricks App, and target failures are logged rather than thrown so a degraded cache never blocks startup.

Choose the request identity

AppKit gives a plugin two identities: the ambient service context (the app's own service principal) and a per-request user context entered with asUser(req), which authenticates on-behalf-of (OBO) using the token the Databricks front door forwards on x-forwarded-access-token. When that header is absent, AppKit's behavior depends entirely on NODE_ENV:

NODE_ENVasUser(req) with no x-forwarded-access-token
developmentlogs a warning, silently runs as the service principal
anything elsethrows AuthenticationError: Missing user token

That throw is right for an app behind the front door, where a missing token means something is broken. It is fatal for an app whose traffic legitimately arrives without one, such as a public tunnel where callers authenticate by email code (@dbx-tools/tunnel) or an authenticated webhook or bot channel. Those apps must not run with NODE_ENV=development only to get the fallback, because that flag also relaxes secure cookies and unlocks other dev-only escape hatches.

identity is that decision, made explicit:

import { identity } from "@dbx-tools/appkit";

const mode = identity.resolveIdentityMode(config.identity, "MY_APP_IDENTITY");

// One call decides whether this request enters `asUser`.
const scoped = identity.useServicePrincipal(mode, req) ? this : this.asUser(req);
const rows = await scoped.executeQuery(sql);
ModeBehavior
user (default)Always OBO. Per-user attribution and per-user Genie / Unity Catalog filters.
service-principalAlways the app's own identity. Needs no OBO scopes and serves any caller.
autoOBO when the request carries a usable token, service principal otherwise.

auto decides per request, not per boot, because one container can serve both doors at once: the tunnel gate and the platform front door share a port. A boot-time flag would have to be wrong for one of them. An unrecognized configured value throws a ConfigurationError rather than falling back, because a typo ("obo", "sp") would otherwise keep serving the very error the option was set to avoid.

Running as the service principal does not change which user owns the request. The caller still arrives on x-forwarded-user / x-forwarded-email (read them with identity.requestUserId() / requestUserEmail()), so memory threads, cache namespaces, and trace attribution stay per-user. Only the Databricks credential is shared. appkit.createApp() stamps the forwarded email, or user id when no email is present, onto every native HTTP request root as OpenTelemetry user.id. @dbx-tools/appkit-mastra exposes the credential choice as its genieIdentity option.

Modules

ModuleResponsibility
appkitcreateApp() / autoConfigure(), plus execution context lookup and initialization.
lakebaseResolverLakebase connection discovery, default picking, optional auto-create, and env application (applyLakebaseEnv() for the full set a pool needs).
pgaddressPermissive Lakebase/Postgres address parser.
bundleBundle and Databricks App environment compatibility helpers.
databricksDatabricks SDK context cancellation adapters.
interceptorLifecycle bridging, teardown callbacks, and process-wide child supervision.
pluginTyped AppKit plugin data, instance, and required-instance lookup.
toolkitShared AppKit toolkit filtering, naming, and entry construction.
provisionCache schema provisioning helpers.
identityOBO-vs-service-principal request identity: modes, resolution, and the forwarded headers.

The shell-facing wrapper for auto-config is @dbx-tools/cli/appkit. Higher-level agent composition is in @dbx-tools/appkit-mastra.

版本与发布记录

@dbx-tools/appkit 共发布 359 个版本,首次发布于 2026/07/18;过去 12 个月发布了 359 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
0.9.206latest2026/11/11今天
0.9.2052026/11/111 天前
0.9.2042026/11/101 天前
0.9.2032026/11/074 天前
0.9.2022026/11/065 天前
0.9.2012026/11/056 天前

关于 @dbx-tools/appkit 的常见问题

@dbx-tools/appkit 是什么?

Node-side helpers for Databricks AppKit applications

如何安装 @dbx-tools/appkit?

在项目目录运行 npm install @dbx-tools/appkit,或使用 pnpm add / yarn add / bun add。

@dbx-tools/appkit 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@dbx-tools/appkit 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@dbx-tools/appkit 可以商用吗?

@dbx-tools/appkit 使用 Apache-2.0 许可。宽松许可:允许商业使用、修改和再分发,并附带明确的专利授权;分发时需保留许可与 NOTICE 文件,修改过的文件要注明改动。以上为概括,不构成法律意见,请以许可原文为准。

@dbx-tools/appkit 的下载量有多少?

根据 npm 官方下载统计,@dbx-tools/appkit 在 2026/10/12 – 2026/11/10 这 30 天内被下载 13,125 次,日均约 438 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@dbx-tools/appkit 还在维护吗?

最新版本 0.9.206 发布于 2026/11/11(今天);至今共发布 359 个版本,其中过去 12 个月发布了 359 个;首次发布于 2026/07/18。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。