Skip to content

feat(appkit): serve named database operations instead of AutoCRUD - #692

Draft
ditadi wants to merge 1 commit into
stack/database-stable/05-client-operationsfrom
stack/database-stable/06-serve-operations
Draft

ditadi wants to merge 1 commit into
stack/database-stable/05-client-operationsfrom
stack/database-stable/06-serve-operations

Conversation

@ditadi

@ditadi ditadi commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor

What did you change, and why?

Why: Generated table routes are a second path around operation authorization, so authored operations must be the only HTTP surface.

Change:

  • database({ routes: databaseRoutes }) mounts the generated registry at /api/database/operations/<name>. Without routes, the plugin serves no HTTP endpoint.
  • Removes AutoCRUD, the api option, the crudRoutes check, the entity client and hooks, the CRUD HTTP facets, and the DatabricksAppKitDatabaseRegistry global, with no compatibility layer.
  • The server's JSON parser skips the operation prefix by routed path, published operation names survive client config redaction, and setup validates the registry before the pool opens.
  • The dev playground, the template, and the docs page use named operations. The playground writes a note and its event in one transaction, and only the author may edit or delete a note.
  • Not flagged: the plugin is beta and an app opts in by passing routes. Beta apps that use AutoCRUD or the entity hooks break by design.

Interface

// server/index.ts
import { createApp, server } from "@databricks/appkit";
import { database } from "@databricks/appkit/beta";

import { databaseRoutes } from "./database/routes.gen";

createApp({ plugins: [server(), database({ routes: databaseRoutes })] });
type IDatabaseConfig = {
  schema?: Schema; // with routes, it must be the schema the registry loads
  routes?: DatabaseRoutes | false; // absent or false: no database HTTP endpoint
};
config/database/schema.ts                       the schema, unchanged
server/database/routes/*.ts                     defineOperation exports, one or more per file
server/database/policies.ts                     shared policies; any module outside routes/
server/database/routes.gen.ts                   generated, committed
shared/appkit-types/database-operations.d.ts    generated, committed

Removed, with no compatibility layer:

  • database({ api }) with DatabaseApiConfig, DatabaseApiWriteOperation, and DatabaseApiWritesConfig, and the AutoCRUD routes under /api/database/<table>.
  • databaseApi.list, get, create, update, and remove, and useDatabaseList, useDatabaseRecord, useDatabaseCreate, useDatabaseUpdate, useDatabaseDelete, and invalidateDatabaseReads.
  • The DatabricksAppKitDatabaseRegistry global that the entity hooks read.

How do you know it works?

Testing: operations.integration.test.ts drives createApp over real HTTP with a fake workspace and an in-memory database (15 tests). Removed routes answer 404, authentication precedes body parsing, policies and transactions hold, and no sentinel reaches spans or logs. pnpm test passes (309 files, 5808 tests, 1 skipped). pnpm build && pnpm docs:build, pnpm check && pnpm -r typecheck, typecheck:database, and the source-less bundle check pass. A Playwright run of the built playground with fake identity and database edges created a note (201), refreshed the list (200), wrote nothing on a forced failure (500), refused a non-author (403), and admitted the author (200). Live Databricks Apps identity and Lakebase are not verified, and the template's --run smoke runs in CI only.


Stack created with GitHub Stacks CLI • Give Feedback 💬

database({ routes }) mounts the generated registry under

/api/database/operations/<name>; without routes the plugin serves no

HTTP endpoint. The table routes, the api and crudRoutes options, the

entity client and hooks, and their HTTP facets are removed.

The playground, template, and docs move to named operations. The

playground writes a note and its event in one transaction, and only

the author may change or delete the note.

Signed-off-by: ditadi <victordperd@gmail.com>
@ditadi
ditadi added this pull request to stack #693 October 23, 2026 15:01
@github-actions

Copy link
Copy Markdown
Contributor

📦 Bundle size report

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

@databricks/appkit

npm tarball (packed): 1.3 MB (+28 KB) — gzipped download (dist + bin; excludes release-only docs/NOTICE).

dist raw gzip
JS (runtime) 1.3 MB (+20 KB) 467 KB (+9.2 KB)
Type declarations 476 KB (+13 KB) 172 KB (+4.1 KB)
Source maps 2.6 MB (+58 KB) 877 KB (+21 KB)
Other 11 KB 3.7 KB
Total 4.3 MB (+91 KB) 1.5 MB (+34 KB)
Per-entry composition (own code — deps external (as shipped))
Entry Initial (gz) Lazy (gz) Total (gz) node_modules (min) Own code (min)
. 111 KB (+4.7 KB) 2.5 KB 113 KB (+4.7 KB) external 357 KB (+14 KB)
./beta 104 KB (+1.2 KB) 485 B 104 KB (+1.2 KB) external 310 KB (+1.6 KB)
./testing 45 KB (+312 B) 37 KB (+4.4 KB) 82 KB (+4.7 KB) external 237 KB (+14 KB)
./tsdown 520 B 0 B 520 B external 813 B
./type-generator 30 KB (+4.2 KB) 0 B 30 KB (+4.2 KB) external 86 KB (+12 KB)

Chunks:

Entry Chunk Load Size (gz)
. index.js initial 106 KB
. utils.js initial 4.6 KB
. remote-tunnel-manager.js lazy 2.5 KB
./beta beta.js initial 84 KB
./beta modular.js initial 6.0 KB
./beta stream-manager.js initial 5.9 KB
./beta service-context.js initial 4.2 KB
./beta databricks.js initial 3.3 KB
./beta client.js initial 647 B
./beta index.js initial 20 B
./beta supervisor-api.js lazy 192 B
./beta databricks.js lazy 178 B
./beta index.js lazy 115 B
./testing manifest.js initial 32 KB
./testing index.js initial 10 KB
./testing wide-event-emitter.js initial 2.9 KB
./testing index.js lazy 32 KB
./testing remote-tunnel-manager.js lazy 2.5 KB
./testing utils.js lazy 1.8 KB
./tsdown index.js initial 520 B
./type-generator index.js initial 30 KB

@databricks/appkit-ui

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

dist raw gzip
JS (runtime) 416 KB (-12 KB) 141 KB (-5.3 KB)
Type declarations 243 KB (-24 KB) 90 KB (-8.8 KB)
Source maps 813 KB (-40 KB) 271 KB (-15 KB)
CSS 16 KB 3.2 KB
Total 1.5 MB (-77 KB) 505 KB (-29 KB)
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 2.4 KB (+264 B) 0 B 2.4 KB (+264 B) 0 B 5.2 KB (+159 B)
./react 433 KB 49 KB 481 KB 1.3 MB 177 KB
./react/beta 3.6 KB (-1.2 KB) 0 B 3.6 KB (-1.2 KB) 0 B 8.4 KB (-4.0 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 2.4 KB
./react index.js initial 431 KB
./react tslib initial 2.1 KB
./react apache-arrow lazy 49 KB
./react/beta beta.js initial 3.6 KB

@github-actions

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 39974632132 -R databricks/appkit -n appkit-template-0.89.0-pr.d7dce6d-stack-database-stable-06-serve-operations-692 -D appkit-pr-692 \
  && unzip -o "appkit-pr-692/appkit-template-0.89.0-pr.d7dce6d-stack-database-stable-06-serve-operations-692.zip" -d "appkit-pr-692" \
  && databricks apps init --template "appkit-pr-692"

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.

This branch has not been deployed

No deployments
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.

1 participant