Skip to content

feat(playground): add a DatabasePlugin example - #529

Merged
ditadi merged 4 commits into
mainfrom
stack/database-mvp/05-integration
Sep 24, 2026
Merged

ditadi merged 4 commits into
mainfrom
stack/database-mvp/05-integration

Conversation

@ditadi

@ditadi ditadi commented Aug 10, 2026 •

Copy link
Copy Markdown
Contributor

Stack

The four PRs above have landed, so this one targets main and the diff shown here is the whole remaining delta.

What

The last PR in the series. No SDK behaviour changes here: this is the dev-playground page that runs the whole plugin against a live Postgres, plus one integration test that asserts the assembled stack from the outside.

The page is a small board/annotation app — three tables, a two-edge include, all three hook points, and a private column — chosen so every claim the earlier PRs make is visible rather than described.

Changes

The schema (config/database/schema.ts)

boards → notes → note_events, which is enough to exercise a nested include. notes.author_email is .private(), so it is writable from server code and refused on the wire — the page shows both halves of that.

The plugin does not create tables. The app expects them to exist, and the page says so when they do not.

The registration passes no schema. #582 made database() load the named schema export from config/database/schema.ts, which is where this file already sits, so the example shows that path rather than importing the schema by hand.

The registration (server/index.ts)

Registered conditionally on LAKEBASE_ENDPOINT, the same way lakebase() already is in this file, so the playground still boots without a database configured.

api: { writes: { tables: ["boards", "notes"] } } generates reads for all three tables but accepts HTTP writes only on boards and notes. The audit trail is written by a hook and never through the API, which is the exposure decision from #527 shown rather than explained.

The notes hooks use all three points:

  • beforeCreate runs an agent to redact personal names from the body before the insert, so the unredacted text never reaches the table, and stamps the private author_email. This is the intended shape of the hook API: a hook is server code and can call any other plugin. The model call is bounded by AbortSignal.timeout(10_000), below the 30-second transaction deadline, and empty or errored output rolls the mutation back rather than falling through to the original text. The agent itself lives in server/agents/redactor/agent.ts, the same folder convention the playground's other code agents use.
  • serialize ships a 120-character preview on the list route and the whole body on the detail route.
  • afterCreate writes the note_events row inside the insert's own transaction, so the note and the event describing it commit together or not at all.

The page (client/src/routes/database.route.tsx)

A board explorer over the generated routes, a panel that traces a write through its hooks, and a probe that fires the requests the plugin is supposed to refuse — writing a private column, an unknown field, an over-large limit — and shows the status and body that come back.

Every request the page makes goes through a generated route, including the nested include, which it reaches with ?include=. An earlier revision carried two hand-written routes alongside them; they are gone, so nothing here competes with the generated API.

One integration test (plugins/database/tests/mvp.integration.test.ts)

Drives the plugin through its real assembly with only the driver mocked: config in, routes registered, request in, response out. It is the test that would catch a regression the unit suites each miss individually.

Review feedback

The three automated findings on the first revision are addressed in the code:

  • The redaction hook no longer falls back to the unredacted body. Empty or whitespace-only model output throws, so the mutation rolls back.
  • The model call inside the transaction is bounded by a 10-second abort signal, and the hook re-checks the signal after the await because a cancelled stream can return accumulated text instead of throwing.
  • The two hand-written routes that returned raw driver error text are gone; the page uses the generated routes, which sanitize their errors.

Verification

  • pnpm test — 5024 passing, 1 skipped
  • pnpm -r typecheck — clean across all packages
  • pnpm run generate:types, pnpm run sync:template, and pnpm run docs:build produce no drift
  • Exercised manually against a live Lakebase instance: reads, writes, the hook chain, the redaction agent, the transactional rollback, and each refusal in the probe

Comment thread apps/dev-playground/server/index.ts Outdated
Comment thread apps/dev-playground/server/index.ts
Comment thread apps/dev-playground/server/index.ts Outdated
Base automatically changed from stack/database-mvp/04-mutations-hooks to main September 8, 2026 14:28
@ditadi
ditadi force-pushed the stack/database-mvp/05-integration branch from 5d88bf2 to 60dab5b Compare September 8, 2026 15:33
@github-actions

github-actions Bot commented Sep 8, 2026 •

Copy link
Copy Markdown
Contributor

📦 Bundle size report

Compared against bundle-size-baseline.json (main).

@databricks/appkit

npm tarball (packed): 1.2 MB (-34 B) — gzipped download (dist + bin; excludes release-only docs/NOTICE).

dist raw gzip
JS (runtime) 1.2 MB (-86 B) 425 KB (-7 B)
Type declarations 443 KB 161 KB
Source maps 2.3 MB (-20 B) 797 KB (-14 B)
Other 11 KB 3.7 KB
Total 4.0 MB (-106 B) 1.4 MB (-21 B)
Per-entry composition (own code — deps external (as shipped))
Entry Initial (gz) Lazy (gz) Total (gz) node_modules (min) Own code (min)
. 96 KB 2.5 KB 98 KB external 314 KB
./beta 93 KB 457 B 93 KB external 281 KB
./testing 38 KB 30 KB (+2 B) 69 KB (+2 B) external 201 KB
./tsdown 520 B 0 B 520 B external 813 B
./type-generator 23 KB 0 B 23 KB external 65 KB

Chunks:

Entry Chunk Load Size (gz)
. index.js initial 92 KB
. utils.js initial 4.0 KB
. remote-tunnel-manager.js lazy 2.5 KB
./beta beta.js initial 77 KB
./beta stream-manager.js initial 5.8 KB
./beta wide-event-emitter.js initial 3.2 KB
./beta databricks.js initial 3.2 KB
./beta configuration.js initial 2.3 KB
./beta service-context.js initial 1.3 KB
./beta client.js initial 434 B
./beta client-options.js initial 219 B
./beta supervisor-api.js lazy 192 B
./beta databricks.js lazy 142 B
./beta index.js lazy 123 B
./testing manifest.js initial 26 KB
./testing index.js initial 9.9 KB
./testing wide-event-emitter.js initial 2.9 KB
./testing index.js lazy 26 KB
./testing remote-tunnel-manager.js lazy 2.5 KB
./testing utils.js lazy 1.2 KB
./tsdown index.js initial 520 B
./type-generator index.js initial 23 KB

@databricks/appkit-ui

npm tarball (packed): 350 KB (+4 B) — gzipped download (dist + bin; excludes release-only docs/NOTICE).

dist raw gzip
JS (runtime) 395 KB 132 KB
Type declarations 229 KB 84 KB (+1 B)
Source maps 766 KB 253 KB
CSS 16 KB 3.2 KB
Total 1.4 MB 472 KB (+1 B)
Per-entry composition (consumer bundle — deps bundled, peerDeps external)
Entry Initial (gz) Lazy (gz) Total (gz) node_modules (min) Own code (min)
./js 5.3 KB 49 KB 55 KB 208 KB 14 KB
./js/beta 20 B 0 B 20 B 0 B 0 B
./react 432 KB 49 KB 481 KB 1.3 MB 177 KB
./react/beta 1.0 KB 0 B 1.0 KB 0 B 1.9 KB

Chunks:

Entry Chunk Load Size (gz)
./js index.js initial 5.2 KB
./js chunk initial 120 B
./js apache-arrow lazy 49 KB
./js/beta beta.js initial 20 B
./react index.js initial 430 KB
./react tslib initial 2.1 KB
./react apache-arrow lazy 49 KB
./react/beta beta.js initial 1.0 KB

@github-actions

github-actions Bot commented Sep 8, 2026 •

Copy link
Copy Markdown
Contributor

🤖 AppKit PR bot

🔬 Run evals

Start an eval for this PR from the evals-monitor app: Go to Evals Monitor →

📦 Try this PR's app template

Scaffolds a new app from this PR's SDK build. Run it in any folder (requires the GitHub CLI — gh auth login — and the Databricks CLI):

gh run download 35989338965 -R databricks/appkit -n appkit-template-0.77.0-pr.bec3a21-stack-database-mvp-05-integration-529 -D appkit-pr-529 \
  && unzip -o "appkit-pr-529/appkit-template-0.77.0-pr.bec3a21-stack-database-mvp-05-integration-529.zip" -d "appkit-pr-529" \
  && databricks apps init --template "appkit-pr-529"

The template pins @databricks/appkit and @databricks/appkit-ui to tarballs built from this branch, so the scaffolded app runs against this PR's code.

Comment thread apps/dev-playground/server/index.ts Outdated
@ditadi
ditadi force-pushed the stack/database-mvp/05-integration branch from 4511f37 to d2e8191 Compare September 23, 2026 22:23
@ditadi
ditadi requested a review from MarioCadenas September 23, 2026 22:23
Exercise the typed client, the generated routes, and the three hook points against a live
Postgres, and cover the assembled stack with one integration test.

Signed-off-by: ditadi <victordperd@gmail.com>
Signed-off-by: ditadi <victordperd@gmail.com>
Addresses review feedback on the createAgent call sitting in index.ts:
the redactor now lives at server/agents/redactor/agent.ts, the same
folder convention the other code agents use, and index.ts imports it.

Also drops the explicit schema from the registration. #582 made
database() load config/database/schema.ts, which is exactly where the
playground's schema already lives, so the example now shows that path
instead of importing the schema by hand.

Signed-off-by: ditadi <victordperd@gmail.com>
PR #582 switched createDatabaseState to initializeLakebasePool, but this
branch's integration test still mocked createLakebasePool. The mock
factory no longer supplied initializeLakebasePool, so every test failed
with SETUP_FAILED once the branch merged with main.

Signed-off-by: ditadi <victordperd@gmail.com>
@ditadi
ditadi force-pushed the stack/database-mvp/05-integration branch from 5865f08 to cad91ec Compare September 24, 2026 10:48
Comment thread apps/dev-playground/server/index.ts Outdated
@ditadi
ditadi merged commit af7a8c0 into main Sep 24, 2026
10 checks passed
@ditadi
ditadi deleted the stack/database-mvp/05-integration branch September 24, 2026 12:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants