Skip to content

Databricks Apps (AppKit)

Skill: databricks-app-appkit

You can build full-stack TypeScript/React applications on the Databricks Apps platform using AppKit — a framework that gives you type-safe SQL queries, ECharts-based visualization components, tRPC for server-side logic, and Lakebase PostgreSQL for persistent storage. Ask your AI coding assistant to scaffold a project and it will generate the init config, SQL query files, typed React components, and deployment config in one pass.

“Scaffold an AppKit analytics app that connects to my SQL warehouse and shows a bar chart of monthly revenue from catalog.schema.orders”

Terminal window
# Initialize the project with analytics features
databricks apps init my-dashboard \
--features analytics \
--set analytics.sql-warehouse.id=abc123def456
-- config/queries/monthly_revenue.sql
SELECT
DATE_TRUNC('month', order_date) AS month,
SUM(total_amount) AS revenue,
COUNT(*) AS order_count
FROM catalog.schema.orders
WHERE order_date >= :start_date
GROUP BY DATE_TRUNC('month', order_date)
ORDER BY month
client/src/App.tsx
import { BarChart, PageLayout } from "@databricks/appkit-ui";
export default function App() {
return (
<PageLayout title="Revenue Dashboard">
<BarChart
queryKey="monthly_revenue"
params=\{{ start_date: "2024-01-01" }}
xKey="month"
yKey="revenue"
colors={["#FF3621"]}
/>
</PageLayout>
);
}

Key decisions:

  • SQL files in config/queries/ — AppKit auto-generates TypeScript types from your SQL. Reference queries by filename (queryKey="monthly_revenue"), never write manual fetch logic.
  • npm run typegen before UI code — types don’t exist until you run typegen. Write SQL first, generate types, then build the React components.
  • ECharts components via propsBarChart, LineChart, DataTable are configured through props (xKey, yKey, colors), not children. These are not Recharts.
  • Colon-prefix parameters:start_date in SQL becomes a typed prop in the component. AppKit binds them at execution time.

“Add a Lakebase feature to my AppKit app so users can save and load dashboard preferences”

Terminal window
databricks apps init my-app \
--features analytics,lakebase \
--set analytics.sql-warehouse.id=abc123 \
--set lakebase.postgres.branch=main
// server/server.ts — tRPC mutation for Lakebase writes
import { initTRPC } from "@trpc/server";
import { getExecutionContext } from "@databricks/appkit";
import { z } from "zod";
const t = initTRPC.create();
export const appRouter = t.router({
savePreference: t.procedure
.input(z.object({ key: z.string(), value: z.string() }))
.mutation(async ({ input }) => {
const { lakebasePool } = getExecutionContext();
await lakebasePool.query(
"INSERT INTO preferences (key, value) VALUES ($1, $2) ON CONFLICT (key) DO UPDATE SET value = $2",
[input.key, input.value]
);
return { success: true };
}),
});

Use SQL query files for warehouse reads (analytics) and tRPC + Lakebase for writes (mutations). Never use tRPC for SELECT queries against the SQL warehouse — useAnalyticsQuery handles that with caching and type safety.

“Add a tRPC endpoint that sends a prompt to my model serving endpoint and returns the response”

queryModel: t.procedure
.input(z.object({ prompt: z.string() }))
.query(async ({ input: { prompt } }) => {
const { serviceDatabricksClient: client } = getExecutionContext();
return await client.servingEndpoints.query({
name: "my-agent-endpoint",
messages: [{ role: "user", content: prompt }],
});
}),

getExecutionContext() provides a pre-authenticated Databricks client — no manual token management. Use it for any Databricks API call: serving endpoints, jobs, MLflow, Unity Catalog.

“Validate and deploy my AppKit app”

Terminal window
# Validate app.yaml and config
databricks apps validate --profile my-workspace
# Deploy
databricks apps deploy my-dashboard --profile my-workspace

Always validate before deploying. The validator catches missing permissions, invalid SQL warehouse references, and malformed app.yaml before you hit runtime errors.

  • Writing UI before typegen — types in appKitTypes.d.ts don’t exist until you run npm run typegen. Write SQL files first, generate types, then build React components. Skipping this gives you any types and no autocomplete.
  • Using tRPC for SQL SELECTsuseAnalyticsQuery connects SQL files to components with caching and pagination built in. tRPC is for mutations, API calls, and server-side logic only.
  • SQL numbers arriving as strings — Databricks SQL may return numeric columns as strings in the TypeScript layer. Always convert with Number(row.amount) before math or chart rendering.
  • Recharts patterns on ECharts components — AppKit charts are ECharts-based and use props (xKey, yKey, colors), not JSX children. Passing <Bar dataKey="value" /> children does nothing.