Skip to content

Commit 581a453

Browse files
authored
feat(appkit): support Lakebase Autoscaling x Apps integration natively (#132)
* feat(appkit): support zero-config Lakebase Autoscaling x Apps integration * chore: update template to avoid perms issues * docs: simplify Lakebase database permissions section Restructure to prioritize the UI-based databricks_superuser flow as the primary path, clarify DML vs DDL access in the local dev steps, and wrap the fine-grained SQL script in a collapsible details block. Signed-off-by: Pawel Kosiec <pawel.kosiec@databricks.com> * fix: align ResourceFieldEntry types with JSON schema and update Lakebase docs Add missing localOnly, value, resolve, and examples fields to all ResourceFieldEntry TypeScript types. Fix bundleIgnore description to reference databricks.yml instead of app.yaml. Simplify Lakebase local dev setup docs since CLI now auto-generates .env. Signed-off-by: Pawel Kosiec <pawel.kosiec@databricks.com> --------- Signed-off-by: Pawel Kosiec <pawel.kosiec@databricks.com>
1 parent d84863c commit 581a453

25 files changed

Lines changed: 502 additions & 218 deletions

docs/docs/api/appkit/Enumeration.ResourceType.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,14 @@ JOB: "job";
4444

4545
***
4646

47+
### POSTGRES
48+
49+
```ts
50+
POSTGRES: "postgres";
51+
```
52+
53+
***
54+
4755
### SECRET
4856

4957
```ts

docs/docs/api/appkit/Interface.ResourceFieldEntry.md

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,16 @@ Single-value types use one key (e.g. id); multi-value types (database, secret) u
55

66
## Properties
77

8+
### bundleIgnore?
9+
10+
```ts
11+
optional bundleIgnore: boolean;
12+
```
13+
14+
When true, this field is excluded from Databricks bundle configuration (databricks.yml) generation.
15+
16+
***
17+
818
### description?
919

1020
```ts
@@ -15,10 +25,50 @@ Human-readable description for this field
1525

1626
***
1727

18-
### env
28+
### env?
1929

2030
```ts
21-
env: string;
31+
optional env: string;
2232
```
2333

2434
Environment variable name for this field
35+
36+
***
37+
38+
### examples?
39+
40+
```ts
41+
optional examples: string[];
42+
```
43+
44+
Example values showing the expected format for this field
45+
46+
***
47+
48+
### localOnly?
49+
50+
```ts
51+
optional localOnly: boolean;
52+
```
53+
54+
When true, this field is only generated for local .env files. The Databricks Apps platform auto-injects it at deploy time.
55+
56+
***
57+
58+
### resolve?
59+
60+
```ts
61+
optional resolve: string;
62+
```
63+
64+
Named resolver prefixed by resource type (e.g., 'postgres:host'). The CLI resolves this value during the init prompt flow.
65+
66+
***
67+
68+
### value?
69+
70+
```ts
71+
optional value: string;
72+
```
73+
74+
Static value for this field. Used when no prompted or resolved value exists.

docs/docs/api/appkit/TypeAlias.ResourcePermission.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ type ResourcePermission =
1111
| UcFunctionPermission
1212
| UcConnectionPermission
1313
| DatabasePermission
14+
| PostgresPermission
1415
| GenieSpacePermission
1516
| ExperimentPermission
1617
| AppPermission;
-42.6 KB
Binary file not shown.
-60.4 KB
Binary file not shown.
-47.4 KB
Binary file not shown.
-106 KB
Binary file not shown.
-41.1 KB
Binary file not shown.

docs/docs/plugins/lakebase.md

Lines changed: 119 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -4,98 +4,30 @@ sidebar_position: 4
44

55
# Lakebase plugin
66

7-
:::info
8-
Currently, the Lakebase plugin currently requires a one-time manual setup to connect your Databricks App with your Lakebase database. An automated setup process is planned for an upcoming future release.
9-
:::
10-
117
Provides a PostgreSQL connection pool for Databricks Lakebase Autoscaling with automatic OAuth token refresh.
128

139
**Key features:**
1410
- Standard `pg.Pool` compatible with any PostgreSQL library or ORM
1511
- Automatic OAuth token refresh (1-hour tokens, 2-minute refresh buffer)
1612
- Token caching to minimize API calls
1713
- Built-in OpenTelemetry instrumentation (query duration, pool connections, token refresh)
14+
- AppKit logger configured by default for query and connection events
1815

19-
## Setting up Lakebase
20-
21-
Before using the plugin, you need to connect your Databricks App's service principal to your Lakebase database.
22-
23-
### 1. Find your app's service principal
24-
25-
Create a Databricks App from the UI (`Compute > Apps > Create App > Create a custom app`). Navigate to the **Environment** tab and note the `DATABRICKS_CLIENT_ID` value — this is the service principal that will connect to your Lakebase database.
26-
27-
![App environment tab](./assets/lakebase-setup/step-1.png)
28-
29-
### 2. Find your Project ID and Branch ID
16+
## Getting started with the Lakebase
3017

31-
Create a new Lakebase Postgres Autoscaling project. Navigate to your Lakebase project's branch details and switch to the **Compute** tab. Note the **Project ID** and **Branch ID** from the URL.
18+
The easiest way to get started with the Lakebase plugin is to use the Databricks CLI to create a new Databricks app with AppKit installed and the Lakebase plugin.
3219

33-
![Branch details](./assets/lakebase-setup/step-2.png)
20+
### Prerequisites
3421

35-
### 3. Find your endpoint
36-
37-
Use the Databricks CLI to list endpoints for the branch. Note the `name` field from the output — this is your `LAKEBASE_ENDPOINT` value.
38-
39-
```bash
40-
databricks postgres list-endpoints projects/{project-id}/branches/{branch-id}
41-
```
42-
43-
Example output:
44-
45-
```json
46-
[
47-
{
48-
"create_time": "2026-02-19T12:13:02Z",
49-
"name": "projects/{project-id}/branches/{branch-id}/endpoints/primary"
50-
}
51-
]
52-
```
22+
- [Node.js](https://nodejs.org) v22+ environment with `npm`
23+
- Databricks CLI (v0.287.0 or higher): install and configure it according to the [official tutorial](https://docs.databricks.com/aws/en/dev-tools/cli/tutorial).
24+
- A new Databricks app with AppKit installed. See [Bootstrap a new Databricks app](../index.md#quick-start-options) for more details.
5325

54-
### 4. Get connection parameters
26+
### Steps
5527

56-
Click the **Connect** button on your Lakebase branch and copy the `PGHOST` and `PGDATABASE` values for later.
57-
58-
![Connect dialog](./assets/lakebase-setup/step-4.png)
59-
60-
### 5. Grant access to the service principal
61-
62-
Navigate to the **SQL Editor** tab on your Lakebase branch. Run the following SQL against the `databricks_postgres` database, replacing the service principal ID in the `DECLARE` block with the `DATABRICKS_CLIENT_ID` value from step 1:
63-
64-
```sql
65-
CREATE EXTENSION IF NOT EXISTS databricks_auth;
66-
67-
DO $$
68-
DECLARE
69-
sp TEXT := 'your-service-principal-id'; -- Replace with DATABRICKS_CLIENT_ID from Step 1
70-
BEGIN
71-
-- Create service principal role
72-
PERFORM databricks_create_role(sp, 'SERVICE_PRINCIPAL');
73-
74-
-- Connection and schema access
75-
EXECUTE format('GRANT CONNECT ON DATABASE "databricks_postgres" TO %I', sp);
76-
EXECUTE format('GRANT ALL ON SCHEMA public TO %I', sp);
77-
78-
-- Privileges on existing objects
79-
EXECUTE format('GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO %I', sp);
80-
EXECUTE format('GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public TO %I', sp);
81-
EXECUTE format('GRANT ALL PRIVILEGES ON ALL FUNCTIONS IN SCHEMA public TO %I', sp);
82-
EXECUTE format('GRANT ALL PRIVILEGES ON ALL PROCEDURES IN SCHEMA public TO %I', sp);
83-
84-
-- Default privileges on future objects you create
85-
EXECUTE format('ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON TABLES TO %I', sp);
86-
EXECUTE format('ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON SEQUENCES TO %I', sp);
87-
EXECUTE format('ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON FUNCTIONS TO %I', sp);
88-
EXECUTE format('ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON ROUTINES TO %I', sp);
89-
END $$;
90-
```
91-
92-
![SQL Editor](./assets/lakebase-setup/step-5.png)
93-
94-
### 6. Verify the role
95-
96-
Navigate to the **Roles & Databases** tab and confirm the role is visible. You may need to fully refresh the page.
97-
98-
![Roles & Databases tab](./assets/lakebase-setup/step-6.png)
28+
1. Firstly, create a new Lakebase Postgres Autoscaling project according to the [Get started documentation](https://docs.databricks.com/aws/en/oltp/projects/get-started).
29+
1. To add the Lakebase plugin to your project, run the `databricks apps init` command and interactively select the **Lakebase** plugin. The CLI will guide you through picking a Lakebase project, branch, and database.
30+
- When asked, select **Yes** to deploy the app to Databricks Apps right after its creation.
9931

10032
## Basic usage
10133

@@ -107,33 +39,6 @@ await createApp({
10739
});
10840
```
10941

110-
## Environment variables
111-
112-
The required environment variables:
113-
114-
| Variable | Description |
115-
|---|---|
116-
| `PGHOST` | Lakebase host |
117-
| `PGDATABASE` | Database name |
118-
| `LAKEBASE_ENDPOINT` | Endpoint resource path (e.g. `projects/.../branches/.../endpoints/...`) |
119-
| `PGSSLMODE` | TLS mode — set to `require` |
120-
121-
Ensure that those environment variables are set both for local development (`.env` file) and for deployment (`app.yaml` file):
122-
123-
```yaml
124-
env:
125-
- name: LAKEBASE_ENDPOINT
126-
value: projects/{project-id}/branches/{branch-id}/endpoints/primary
127-
- name: PGHOST
128-
value: {your-lakebase-host}
129-
- name: PGDATABASE
130-
value: databricks_postgres
131-
- name: PGSSLMODE
132-
value: require
133-
```
134-
135-
For the full configuration reference (SSL, pool size, timeouts, logging, ORM examples), see the [`@databricks/lakebase` README](https://github.com/databricks/appkit/blob/main/packages/lakebase/README.md).
136-
13742
## Accessing the pool
13843

13944
After initialization, access Lakebase through the `AppKit.lakebase` object:
@@ -143,9 +48,17 @@ const AppKit = await createApp({
14348
plugins: [server(), lakebase()],
14449
});
14550

146-
// Direct query (parameterized)
51+
await AppKit.lakebase.query(`CREATE SCHEMA IF NOT EXISTS app`);
52+
53+
await AppKit.lakebase.query(`CREATE TABLE IF NOT EXISTS app.orders (
54+
id SERIAL PRIMARY KEY,
55+
user_id VARCHAR(255) NOT NULL,
56+
amount DECIMAL(10, 2) NOT NULL,
57+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
58+
)`);
59+
14760
const result = await AppKit.lakebase.query(
148-
"SELECT * FROM orders WHERE user_id = $1",
61+
"SELECT * FROM app.orders WHERE user_id = $1",
14962
[userId],
15063
);
15164

@@ -157,7 +70,32 @@ const ormConfig = AppKit.lakebase.getOrmConfig(); // { host, port, database, ..
15770
const pgConfig = AppKit.lakebase.getPgConfig(); // pg.PoolConfig
15871
```
15972

160-
## Configuration options
73+
## Configuration
74+
75+
### Environment variables
76+
77+
The required environment variables are:
78+
79+
| Variable | Description |
80+
|---|---|
81+
| `LAKEBASE_ENDPOINT` | Endpoint resource path (e.g. `projects/.../branches/.../endpoints/...`) |
82+
| `PGHOST` | Lakebase host (auto-injected in production by the `postgres` Databricks Apps resource) |
83+
| `PGDATABASE` | Database name (auto-injected in production by the `postgres` Databricks Apps resource) |
84+
| `PGSSLMODE` | TLS mode - set to `require` (auto-injected in production by the `postgres` Databricks Apps resource) |
85+
86+
When deployed to Databricks Apps with a `postgres` database resource configured, `PGHOST`, `PGDATABASE`, `PGSSLMODE`, `PGUSER`, `PGPORT`, and `PGAPPNAME` are automatically injected by the platform. Only `LAKEBASE_ENDPOINT` must be set explicitly:
87+
88+
```yaml
89+
env:
90+
- name: LAKEBASE_ENDPOINT
91+
valueFrom: postgres
92+
```
93+
94+
For local development, the `.env` file is automatically generated by `databricks apps init` with the correct values for your Lakebase project.
95+
96+
For the full configuration reference (SSL, pool size, timeouts, logging, ORM examples), see the [`@databricks/lakebase` README](https://github.com/databricks/appkit/blob/main/packages/lakebase/README.md).
97+
98+
### Pool configuration
16199

162100
Pass a `pool` object to override any defaults:
163101

@@ -174,3 +112,75 @@ await createApp({
174112
],
175113
});
176114
```
115+
116+
## Database Permissions
117+
118+
When you create the app with the Lakebase resource using the [Getting started](#getting-started-with-the-lakebase) guide, the Service Principal is automatically granted `CONNECT_AND_CREATE` permission on the `postgres` resource. This lets the Service Principal connect to the database and create new objects, but **not access any existing schemas or tables.**
119+
120+
### Local development
121+
122+
To develop locally against a deployed Lakebase database:
123+
124+
1. **Deploy the app first.** The Service Principal creates the database schema and tables on first deploy. Apps generated from `databricks apps init` handle this automatically - they check if tables exist on startup and skip creation if they do.
125+
126+
2. **Grant `databricks_superuser` via the Lakebase UI:**
127+
1. Open the Lakebase Autoscaling UI and navigate to your project's **Branch Overview** page.
128+
2. Click **Add role** (or **Edit role** if your OAuth role already exists).
129+
3. Select your Databricks identity as the principal and check the **`databricks_superuser`** system role.
130+
131+
3. **Run locally** - your Databricks user identity (email) is used for OAuth authentication. The `databricks_superuser` role gives full **DML access** (read/write data) but **not DDL** (creating schemas or tables) - that's why deploying first matters (see note below).
132+
133+
For other users, use the same **Add role** flow in the Lakebase UI to create an OAuth role with `databricks_superuser` for each user.
134+
135+
:::tip
136+
[Postgres password authentication](https://docs.databricks.com/aws/en/oltp/projects/authentication#overview) is a simpler alternative that avoids OAuth role permission complexity. However, it requires you to set up a password for the user in the **Branch Overview** page in the Lakebase Autoscaling UI.
137+
:::
138+
139+
:::info[Why deploy first?]
140+
When the app is deployed, the Service Principal creates schemas and tables and becomes their owner. A `databricks_superuser` has full **DML access** (SELECT, INSERT, UPDATE, DELETE) to these objects, but **cannot run DDL** (CREATE SCHEMA, CREATE TABLE) on schemas owned by the Service Principal. Deploying first ensures all objects exist before local development begins.
141+
:::
142+
143+
### Fine-grained permissions
144+
145+
For most use cases, `databricks_superuser` is sufficient. If you need schema-level grants instead, refer to the official documentation:
146+
147+
- [Manage database permissions](https://docs.databricks.com/aws/en/oltp/projects/manage-roles-permissions)
148+
- [Postgres roles](https://docs.databricks.com/aws/en/oltp/projects/postgres-roles)
149+
150+
<details>
151+
<summary>SQL script for fine-grained grants</summary>
152+
153+
Deploy and run the app at least once before executing these grants so the Service Principal initializes the database schema first.
154+
155+
Replace `subject` with the user email and `schema` with your schema name:
156+
157+
```sql
158+
CREATE EXTENSION IF NOT EXISTS databricks_auth;
159+
160+
DO $$
161+
DECLARE
162+
subject TEXT := 'your-subject'; -- User email like name@databricks.com
163+
schema TEXT := 'your_schema'; -- Replace 'your_schema' with your schema name
164+
BEGIN
165+
-- Create OAuth role for the Databricks identity
166+
PERFORM databricks_create_role(subject, 'USER');
167+
168+
-- Connection and schema access
169+
EXECUTE format('GRANT CONNECT ON DATABASE "databricks_postgres" TO %I', subject);
170+
EXECUTE format('GRANT ALL ON SCHEMA %s TO %I', schema, subject);
171+
172+
-- Privileges on existing objects
173+
EXECUTE format('GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA %s TO %I', schema, subject);
174+
EXECUTE format('GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA %s TO %I', schema, subject);
175+
EXECUTE format('GRANT ALL PRIVILEGES ON ALL FUNCTIONS IN SCHEMA %s TO %I', schema, subject);
176+
EXECUTE format('GRANT ALL PRIVILEGES ON ALL PROCEDURES IN SCHEMA %s TO %I', schema, subject);
177+
178+
-- Default privileges on future objects
179+
EXECUTE format('ALTER DEFAULT PRIVILEGES IN SCHEMA %s GRANT ALL ON TABLES TO %I', schema, subject);
180+
EXECUTE format('ALTER DEFAULT PRIVILEGES IN SCHEMA %s GRANT ALL ON SEQUENCES TO %I', schema, subject);
181+
EXECUTE format('ALTER DEFAULT PRIVILEGES IN SCHEMA %s GRANT ALL ON FUNCTIONS TO %I', schema, subject);
182+
EXECUTE format('ALTER DEFAULT PRIVILEGES IN SCHEMA %s GRANT ALL ON ROUTINES TO %I', schema, subject);
183+
END $$;
184+
```
185+
186+
</details>

0 commit comments

Comments
 (0)