name: 05-appkit-lakebase-wiring
description: >
Wire a Lakebase PostgreSQL backend into an existing AppKit project. Covers database
schema design from a PRD, idempotent DDL, Express API routes with mock fallback,
React data hooks, and local testing. PRD-independent patterns that apply to any
AppKit + Lakebase app. Use after registering the Lakebase plugin via
04-appkit-plugin-add. Triggers on "wire lakebase", "lakebase backend", "CRUD API",
"lakebase tables", "DDL", "database schema design", "useLakebaseData",
"mock fallback", "ConnectionStatus", "replace mock data", "connect frontend to
backend", "API-backed data", "replace static data with database".
license: Apache-2.0
compatibility: Requires Lakebase plugin registered via 04-appkit-plugin-add, Node.js v22+, Databricks CLI >= 0.295.0
allowed-tools: Bash(databricks:) Bash(npm:) Bash(curl:) Bash(node:) Read
clients: [ide_cli, genie_code]
bundle_resource: apps
deploy_verb: apps_deploy
deploy_note: >
Wiring is source editing (server.ts DDL/routes, hooks) — client-agnostic. DDL runs server-side on
first deploy under the app's Service Principal (Deploy-First Pattern), so the SP owns the schema/tables on
both clients — never run DDL locally. IDE: npm run build gates locally, then databricks apps deploy --profile $PROFILE. Genie Code: local npm run build gates are an IDE convenience (no local npm) — skip
them and let the platform build server-side; deploy via runDatabricksCli (omit --profile) or the SDK
fallback in 03-appkit-deploy. Verify via browser ConnectionStatus / apps logs or the OAuth-session test.
coverage: full
metadata:
author: prashanth subrahmanyam
version: "1.1.0"
domain: apps
role: lakebase-wiring
standalone: false
last_verified: "2026-06-02"
volatility: medium
upstream_sources:
- name: "databricks-agent-skills/databricks-lakebase"
repo: "databricks/databricks-agent-skills"
paths:
- "skills/databricks-lakebase/SKILL.md"
relationship: "extended"
last_synced: "2026-04-27"
sync_commit: "manifest-v2-2026-04-22"
- name: "databricks-agent-skills/databricks-apps"
repo: "databricks/databricks-agent-skills"
paths:
- "skills/databricks-apps/SKILL.md"
relationship: "extended"
last_synced: "2026-04-27"
sync_commit: "manifest-v2-2026-04-22"
Wire Lakebase Backend into AppKit
Design a database schema from a PRD, build Express API routes with mock fallback, wire the React frontend to live data, and verify locally.
When to Use
- Wiring a Lakebase PostgreSQL backend into an AppKit app that already has the plugin registered
- Designing database tables from a PRD or feature spec
- Building CRUD API routes with
server.extend() - Replacing static mock data with API-backed data fetching
- Adding a health endpoint and ConnectionStatus indicator
Not for registering the plugin. Use 04-appkit-plugin-add to install and configure the Lakebase plugin first.
Not for deploying. Use 03-appkit-deploy after wiring is complete.
Before You Begin
Prerequisites — verify these before proceeding:
- The Lakebase plugin is registered in
server/server.ts(via04-appkit-plugin-add) - Bundle resources (
postgres_project/postgres_branch/postgres_endpoint) are declared indatabricks.yml app.yamlhasLAKEBASE_ENDPOINTwithvalueFrom: postgresandDB_SCHEMAas a static env varDB_SCHEMAis set to a user-scoped name derived from$APP_NAME(hyphens → underscores, e.g.,jane_d_booking_app)npm run buildpasses with the Lakebase plugin imported
Upstream docs (always check for latest):
npx @databricks/appkit docs "lakebase"
Build system note: AppKit uses
tsdownwithunbundle: truefor server compilation. Each.tsfile inserver/gets its own.jsoutput, and relative imports between them are preserved. You can safely splitserver/server.tsinto multiple files (e.g.,server/mock-data.ts,server/mappers.ts) — they will resolve correctly at runtime. The entry point remainsserver/server.ts.
Working in Genie Code (client routing)
All the code in this skill — DDL, routes, hooks, mappers — is written into the project the same way on both clients. The DDL is intentionally executed server-side on first deploy (the "Deploy-First Pattern" in Step 1d), so it is already client-agnostic. Only the local build/test gates differ:
| IDE/CLI (as written) | Genie Code substitution |
|---|---|
npm run build gates (Steps 2g, 3c2, 4a, 4b) — "You MUST run npm run build" |
IDE-only convenience — there is no local Node toolchain on Genie Code. Skip them; the platform runs the same build server-side on deploy, and TypeScript/import errors surface in databricks apps logs <name>. Wire the code, then deploy and read the logs. |
npm run dev |
not available on Genie Code (and blocked pre-deploy anyway — lakebase() needs platform-injected env) — verify on the deployed app instead |
npx @databricks/appkit docs "lakebase" |
npx absent (P9) — WebFetch https://databricks.github.io/appkit/docs/plugins/lakebase |
databricks … --profile $PROFILE |
run via runDatabricksCli, omit --profile (pre-authenticated) |
local curl … -H "Authorization: Bearer …" health test |
browser ConnectionStatus + apps logs, or the 3-hop OAuth requests.Session() test (see Quick Reference note and 03-appkit-deploy) |
Paths are relative to apps_lakebase/$APP_NAME — on Genie Code that resolves inside your git-cloned workshop project (artifact_root), never the read-only .assistant/skills copy and never /tmp. The apps validate skip and the health-test routing are already flagged inline at Steps 4a2 and Quick Reference. See skills/genie-code-environment for the full manifest.
Decision Defaults
When multiple approaches are valid, use these defaults. Override only if the PRD demands it.
| Decision | Default | Rationale |
|---|---|---|
| Where does mock data live? | server/mock-data.ts — server owns all fallback data |
Client and server are separate builds; cannot import across the boundary |
| Single server file or split? | Split when server.ts exceeds 300 lines. Extract mappers to server/mappers.ts. Count lines after writing: wc -l server/server.ts. |
tsdown unbundle: true preserves relative imports between server files (see note above) |
| Numeric PK or text PK? | Use text PK if the frontend already uses formatted IDs (e.g., "lst-001") everywhere; otherwise bigint identity |
Avoids a format-conversion layer that touches every route and mapper |
| N+1 queries or JOINs? | Application-side joins for <50 rows; SQL JOINs for larger datasets | Simpler code; performance is irrelevant at seed-data scale |
| Client-side or server-side validation? (promos, discounts) | Keep client-side for MVP; migrate to server in a follow-up | Reduces scope of this step |
| Dynamically generated data (available dates, time slots)? | Generate in the mapper function, not a database table | Avoids seeding hundreds of ephemeral rows |
| Mock fallback data format? | camelCase (matching API response after mappers) | Catch block returns data in the same shape as the live path |
| Seed data: parameterized inserts or raw SQL? | Parameterized ($1, $2) iterating over MOCK_ arrays* for all tables when mock-data.ts exists. Raw SQL only when no mock array exists and data is purely numeric. |
Single source of truth; type-safe; avoids escaping bugs |
Step 1: Design the Database Schema
Derive your database schema from the PRD. This step produces DDL and seed data in server/server.ts.
1a. Identify Entities from the PRD
Read the PRD and extract:
- Entities — each major noun becomes a table (e.g.,
bookings,listings,users) - Attributes — each property becomes a column
- Relationships — identify 1:N (FK) and M:N (junction table) relationships
- Data types — map PRD fields to PostgreSQL types using the conventions below
1b. PostgreSQL Type Conventions
For normalization rules, naming conventions, and additional type guidance, see references/database-design-guide.md.
| PRD Concept | PostgreSQL Type | Why |
|---|---|---|
| Unique ID | bigint generated always as identity primary key |
SQL-standard, future-proof (not serial) |
| Short text (name, email, status) | text |
Same performance as varchar(n), no artificial limit |
| Constrained text (status enum) | text + CHECK constraint |
CHECK (status IN ('pending','confirmed','cancelled')) |
| Money / price | numeric(10,2) |
Exact decimal arithmetic (not float) |
| Timestamp | timestamptz default now() |
Always timezone-aware (not timestamp) |
| Date only | date |
Calendar dates without time component |
| Boolean flag | boolean default false |
1 byte (not varchar) |
| Foreign key | bigint references other_table(id) |
Always add an index on FK columns |
1c. Schema Naming
Use DB_SCHEMA (from env var) as the PostgreSQL schema for all objects:
CREATE SCHEMA IF NOT EXISTS ${DB_SCHEMA};
CREATE TABLE IF NOT EXISTS ${DB_SCHEMA}.bookings ( ... );
This prevents collisions when multiple apps share a Lakebase database. All DDL, queries, and grants must use ${DB_SCHEMA} consistently.
1d. Write Idempotent DDL
All DDL runs on every app startup. It must be safe to execute repeatedly.
const AppKit = await createApp({
plugins: [server(), lakebase()],
});
const DB_SCHEMA = process.env.DB_SCHEMA || "app";
await AppKit.lakebase.query(`CREATE SCHEMA IF NOT EXISTS ${DB_SCHEMA}`);
await AppKit.lakebase.query(`
CREATE TABLE IF NOT EXISTS ${DB_SCHEMA}.orders (
id bigint generated always as identity primary key,
customer_name text not null,
amount numeric(10, 2) not null,
status text default 'pending' check (status in ('pending', 'confirmed', 'cancelled')),
created_at timestamptz default now()
)
`);
Index foreign key and frequently filtered columns:
await AppKit.lakebase.query(`
CREATE INDEX IF NOT EXISTS idx_orders_status
ON ${DB_SCHEMA}.orders (status)
`);
Deploy-First Pattern: DDL is written in
server.tsso the Service Principal executes it on first deploy and becomes the owner of the schema, tables, and sequences. This is critical — if you run DDL locally first, your personal identity owns the objects and the SP cannot access them after deployment. The workshop sequence (wire code locally with mock fallback, then deploy so SP creates objects) follows this pattern. Reference: AppKit Lakebase docs - Local development
1e. Seed Data (Count-Check Pattern)
Use a count-check pattern for idempotent seeding. Do NOT use ON CONFLICT DO NOTHING — it fails to prevent duplicates with identity/serial PKs that auto-generate new IDs on every insert.
const seedCheck = await AppKit.lakebase.query(
`SELECT count(*) AS cnt FROM ${DB_SCHEMA}.orders`
);
if (parseInt(seedCheck.rows[0].cnt) === 0) {
await AppKit.lakebase.query(`
INSERT INTO ${DB_SCHEMA}.orders (customer_name, amount, status) VALUES
('Alice', 99.99, 'confirmed'),
('Bob', 45.00, 'pending'),
('Carol', 72.50, 'confirmed')
`);
console.log("[Lakebase] Seed data inserted");
}
Apostrophe escaping: If seed data contains apostrophes (e.g.,
chef's kitchen), use double single quotes in raw SQL:'chef''s kitchen'. Alternatively, use parameterized inserts ($1, $2) which handle escaping automatically — see the Decision Defaults table.
For multi-table seed patterns with foreign keys, see references/multi-table-example.md.
1f. Mock Data Strategy
When the app already has a client-side mockData.ts with static arrays, use this migration pattern:
- Create
server/mock-data.tswith the same data in camelCase format (matching what mappers would produce from DB rows). Export typed arrays. - Import into
server/server.tsfor use in catch-block fallbacks and as the source for seed SQL generation. - Keep
client/src/data/mockData.tsfor type definitions, UI constants (filter options, property type lists), and utility functions. Remove the data arrays once all pages useuseLakebaseData. - Pages switch from
import { LISTINGS } from '../data/mockData'touseLakebaseData<Listing>('/api/listings')— the server returns mock fallback data when Lakebase is unavailable, so the client never needs its own copy of the data.
File ownership after wiring:
| File | Contains | Does NOT contain |
|---|---|---|
server/mock-data.ts |
All data arrays (listings, bookings, etc.) in camelCase | Type definitions, UI constants |
client/src/data/mockData.ts |
Type interfaces, filter-option constants, utility functions | Data arrays (deleted after migration) |
server/server.ts |
DDL, seed logic, routes importing from mock-data.ts |
Inline mock data objects |
This table is prescriptive.
server/mock-data.tsmust NOT define TypeScript interfaces — those belong exclusively inclient/src/data/mockData.ts. The server file exports data arrays only and imports types from the client when feasible, or usesRecord<string, unknown>parameter types with explicit return-type annotations. Duplicating interfaces causes type drift (e.g., serverstatus: stringvs. clientstatus: "confirmed" | "cancelled").
Step 2: Build API Routes
AppKit Lakebase is server-side only — there are no frontend hooks like useAnalyticsQuery. Use server.extend() to add Express routes.
Note: The upstream databricks-agent-skills Lakebase reference uses tRPC for server-side CRUD. This skill uses
server.extend()with Express routes for explicit control over request/response handling, which is simpler for workshop purposes. Both patterns are valid AppKit approaches. If the scaffold generated tRPC boilerplate (from--features lakebase), either pattern works — use whichever is already in your codebase.
2a. Server Setup Pattern
Register custom routes inside the onPluginsReady callback. It runs after the plugins initialize but before the server() plugin starts listening — exactly the window where appkit.server.extend() must attach Express routes. Move the Step 1 DDL and the Step 1e seed inside this same callback (before the extend call) so the schema exists before the server accepts traffic. Do NOT pass autoStart: false and do NOT call AppKit.server.start() yourself:
import { createApp, server, lakebase } from "@databricks/appkit";
const DB_SCHEMA = process.env.DB_SCHEMA || "app";
await createApp({
plugins: [server(), lakebase()],
async onPluginsReady(appkit) {
// DDL + seed (from Step 1) — run before the server accepts traffic
await appkit.lakebase.query(`CREATE SCHEMA IF NOT EXISTS ${DB_SCHEMA}`);
// ... CREATE TABLE / INDEX + count-check seed ...
appkit.server.extend((app) => {
// Register routes here (Steps 2b-2d)
});
},
});
npm run devwill NOT work until after the first deploy. Thelakebase()plugin throwsConfigurationErrorduringcreateApp()whenLAKEBASE_ENDPOINTandPGHOSTare not set. These env vars are injected by the platform after the Lakebase project is provisioned (first deploy). Usenpm run buildonly for local validation at this step — it type-checks and bundles without executing the code. Runtime testing withnpm run devis available after deployment creates the Lakebase project and you populate.envwith connection details (see the Deploy and E2E Test step, Step 7).
Gotcha — register routes in
onPluginsReady, never callserver.start()manually. Theserver()plugin owns the HTTP listener. PassingautoStart: falseand then callingAppKit.server.start()yourself is the stale pattern — it double-listen()s (the plugin still binds the port) and the app crashes on boot (EADDRINUSE). The supported shape is theonPluginsReady(appkit)hook oncreateApp: it fires after plugins init but before the listener binds, soappkit.server.extend(...)routes are attached in time. Theserver()plugin also auto-adds/health— do not register it yourself. Reference: developer docs — AppKitcreateApp(onPluginsReadyhook).
Gotcha — Do NOT annotate
appwith: anyor: Express. AppKit's AST-grep linter blocksanyannotations (no-as-anyrule), and importingExpressfrom theexpressmodule causes TS2345 (type mismatch with AppKit's internal type). Leaveappuntyped inserver.extend((app) => { ... })— TypeScript infers the correct type from the callback signature. For route handler parameters, import and useRequestandResponsetypes fromexpress.
2b. Response Contract
Every data endpoint must return this shape:
{ data: T[], source: "live" | "mock" }
source: "live"— data came from Lakebasesource: "mock"— Lakebase unavailable, returning fallback data
2c. CRUD Route Pattern (with Mock Fallback)
Every route follows try/catch with mock fallback:
import type { Request, Response } from "express";
app.get("/api/orders", async (req: Request, res: Response) => {
try {
const result = await AppKit.lakebase.query(
`SELECT * FROM ${DB_SCHEMA}.orders ORDER BY created_at DESC`
);
console.log(`[Lakebase] /api/orders returned ${result.rows.length} rows`);
res.json({ data: result.rows, source: "live" });
} catch (err) {
console.warn(`[Lakebase] /api/orders fallback: ${err}`);
res.json({
data: [{ id: 1, customer_name: "Demo", amount: 99.99, status: "mock" }],
source: "mock",
});
}
});
For parameterized queries, use $1, $2 placeholders (not string interpolation):
app.get("/api/orders/:id", async (req, res) => {
try {
const result = await AppKit.lakebase.query(
`SELECT * FROM ${DB_SCHEMA}.orders WHERE id = $1`,
[req.params.id]
);
res.json({ data: result.rows, source: "live" });
} catch (err) {
res.json({ data: [], source: "mock" });
}
});
2d. Health Endpoint
Always include a health endpoint:
app.get("/api/health/lakebase", async (req, res) => {
try {
await AppKit.lakebase.query("SELECT 1");
res.json({ status: "connected", source: "live" });
} catch (err) {
res.json({ status: "disconnected", error: String(err), source: "mock" });
}
});
2e. POST/PUT Routes (skip if your app only has GET endpoints)
If your app has POST or PUT routes, add JSON body parsing. Express is bundled inside AppKit — access it exclusively via the app parameter in server.extend():
AppKit.server.extend((app) => {
app.use((req, _res, next) => {
if (req.headers["content-type"]?.includes("application/json") && !req.body) {
let raw = "";
req.on("data", (chunk) => { raw += chunk; });
req.on("end", () => {
try { req.body = JSON.parse(raw); } catch { req.body = {}; }
next();
});
} else { next(); }
});
// POST routes ...
});
Gotcha —
import express: Do notimport expressorrequire("express")just forexpress.json(). It creates a redundant copy alongside AppKit's bundled version and may cause version conflicts. Use the inline parser above instead.
2f. Connection Resilience (optional)
AppKit's Lakebase plugin handles OAuth token rotation and caching automatically. Configure pool settings only if you need additional resilience:
await createApp({
plugins: [
lakebase({
pool: {
max: 10,
connectionTimeoutMillis: 5000,
idleTimeoutMillis: 30000,
},
}),
],
});
Step 2g: Build Gate
You MUST run npm run build and fix all errors before proceeding to Step 3. Backend build errors found after frontend wiring are much harder to diagnose. This gate catches them early.
Line count check: After build passes, run wc -l server/server.ts. If >300 lines, extract mapper functions to server/mappers.ts and re-run build before proceeding.
Step 3: Wire Frontend
Replace all static mock data arrays in React components with API-backed data fetching.
3a. useLakebaseData Hook
You MUST read references/frontend-patterns.md and copy the useLakebaseData hook into client/src/hooks/useLakebaseData.ts. This hook replaces useState/useEffect/fetch boilerplate with a reusable pattern that returns { data, source, error }.
Usage: const { data, source, error } = useLakebaseData<Order>("/api/orders");
3b. ConnectionStatus Component
You MUST read references/frontend-patterns.md and copy the ConnectionStatus component. Place it at the top of every page that fetches data. Pass a context string describing the data (e.g., context="orders").
3c. Replace Static Mock Data
- Replace all static
dataarrays withuseLakebaseDatahook calls - For AppKit chart components (
BarChart,AreaChart,DonutChart,DataTable), use thedataprop with fetched results
Gotcha —
queryKeyrequires the analytics plugin. Do NOT usequeryKeyon chart components. Use thedataprop with fetched results fromuseLakebaseDatainstead.
3c2. Intermediate Build Gate
After rewiring every 2-3 page files, run npm run build (or npx tsc --noEmit). Do not rewrite all pages in a single pass.
When removing a static data import (e.g., import { LISTINGS } from '../data/mockData'), audit every other import in the same file. Symbols like MapPin, User, or helper functions may have only been used alongside the removed data source. If a page previously showed data from PARENT_ARRAY.find() (e.g., a property image on a booking page), the rewrite must either fetch that data via API or explicitly acknowledge the removal.
Rule: removing a data import may orphan UI elements that depended on it. Check imports and visual output after each batch. See references/multi-table-example.md "Cross-Entity Enrichment" for the API pattern.
3d. Defensive Data Handling
You MUST read the "Defensive Data Handling" section in references/frontend-patterns.md. Key rules: coerce DECIMAL with Number(), format DATE with .toISOString().slice(0,10), write mapper functions for snake_case-to-camelCase.
Gotcha — DECIMAL columns are returned as strings by
node-pg."73" + "51"produces"7351"(concatenation, not addition). Always coerce withNumber()in mapper functions.
Gotcha — DATE columns are returned as JS Date objects.
String(date)produces"Fri May 15 2026...". Use.toISOString().slice(0, 10)forYYYY-MM-DDformat.
3e. TypeScript Interfaces for Chart Compatibility
You MUST add [key: string]: unknown index signatures to all interfaces passed to AppKit chart data props. See references/frontend-patterns.md for the pattern.
3f. Post-Wiring Cleanup (optional but recommended)
After all pages are wired to API calls:
Rename
mockData.tsto better reflect its new role:- Move type interfaces to
client/src/data/types.ts - Move utility functions and constants to
client/src/data/constants.ts - Delete the original
mockData.ts(or keep it as a re-export if too many imports to update)
- Move type interfaces to
Import server mapper return types from the client types:
import type { Listing } from "../shared/types"; function mapListingRow(row: any): Listing { ... }This ensures server mapper output matches client expectations at compile time. If a
shared/directory is not feasible, at minimum annotate mapper return types to match the client interface names.
The field-name mismatch class of bugs (e.g., image vs imageUrl) is eliminated when mapper functions declare their return type explicitly.
Step 4: Build Gate and Local Test
4a. Build Gate
cd apps_lakebase/$APP_NAME
npm run build # Must pass with zero errors
Fix TypeScript, ESM, or import errors now. Each deploy cycle takes 3-5 minutes — catching errors locally saves significant time.
4a2. Validate (optional but recommended)
Run the full AppKit validator to catch issues that npm run build alone misses — TypeScript strict mode errors (e.g., app parameter implicitly any), smoke test selector regressions, and resource binding problems:
databricks apps validate --profile $PROFILE
If the smoke test fails because of default selectors ("Minimal Databricks App", "hello world"), update tests/smoke.spec.ts heading and text assertions to match your app's actual content. See testing.md.
Client note — Genie Code:
databricks apps validateis hard-blocked viarunDatabricksCli. Skip it and rely on the server-side build logs after deploy; see03-appkit-deployStep 1 (Client note).
4b. Local Validation (Build Only)
Do NOT run
npm run devat this step. Thelakebase()plugin throwsConfigurationErrorwhenLAKEBASE_ENDPOINTandPGHOSTare not set. These env vars are provisioned by the platform on first deploy.npm run buildis sufficient — it validates all TypeScript, imports, and bundling without executing the code.
cd apps_lakebase/$APP_NAME
npm run build # Must pass with zero errors
Runtime testing (npm run dev, curl endpoints, UI verification) happens after deployment in the Deploy and E2E Test step. After that deploy creates the Lakebase project and you configure .env with connection details, npm run dev works locally with live data.
4c. What to Verify at This Step
Since npm run dev is not available, verify these through npm run build output:
- Zero TypeScript errors (catches incorrect types, missing imports)
- All
@databricks/lakebaseand@databricks/appkitimports resolve - DDL strings, route handlers, and mapper functions compile
- Mock fallback data arrays match the expected API response shape
Gotchas (Summary)
Detailed callouts are embedded inline at the relevant step. This table is a compact reference validated by three independent agent runs.
| Gotcha | Fix | Step |
|---|---|---|
ON CONFLICT DO NOTHING with identity PKs |
Count-check seed pattern | 1e |
import express / require("express") |
Use server.extend((app)) + inline parser |
2e |
Manual AppKit.server.start() / autoStart: false |
Register routes in onPluginsReady(appkit) + appkit.server.extend(...); let server() own the listener (manual start double-binds → EADDRINUSE) |
2a |
lakebase() crashes without env vars (LAKEBASE_ENDPOINT, PGHOST) |
Expected before first deploy. Use npm run build only; npm run dev works after deploy + .env setup |
2a |
| DECIMAL columns → strings | Number() in mappers |
3d |
| DATE columns → Date objects | .toISOString().slice(0,10) |
3d |
queryKey on chart components |
Use data prop instead |
3c |
npm run dev crashes without Lakebase env vars |
Use npm run build only before first deploy |
4b |
Old postgres: resource format |
Use postgres_project/postgres_branch/postgres_endpoint |
Prerequisites |
Stale endpoint hostname in .env |
Re-fetch: databricks postgres get-endpoint ... |
Prerequisites |
AppKit AST-grep linter blocks as any and as unknown as T |
Avoid type assertions; use proper type imports or leave parameters untyped for inference. databricks apps validate runs these rules but npm run build does not |
2a |
| SP permission errors on deploy | Verify bundle resource bindings in databricks.yml |
Prerequisites |
Quick Reference
| Task | Command / Pattern |
|---|---|
| Check live Lakebase docs | npx @databricks/appkit docs "lakebase" |
| Derive schema name | DB_SCHEMA=$(echo "$APP_NAME" | tr '-' '_') |
| Build gate | npm run build (must pass with zero errors) |
| Test health endpoint (after deploy) | curl -s "$APP_URL/api/health/lakebase" -H "Authorization: Bearer $TOKEN" | jq . (IDE only — see note) |
Client note — Genie Code: a plain
curl … -H "Authorization: Bearer $TOKEN"does not work (auth tokenhard-blocked; raw Bearer rejected by AppKit's OAuth gate → 401). Two working paths: (1) browser — open the app URL and read theConnectionStatusindicator (live/mock), plusdatabricks apps logs <name>for[Lakebase]query lines; (2) programmatic — hit/api/health/lakebasevia the 3-hop OAuthrequests.Session()pattern in03-appkit-deploy"Testing Deployed App APIs" (Client note).
See Also
- Upstream platform skills:
databricks-lakebaseanddatabricks-apps(both tracked inupstream_sources). - AppKit Lakebase plugin docs: databricks.github.io/appkit/docs/plugins/lakebase