Skip to content

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

Open
ditadi wants to merge 5 commits into
stack/database-stable/05-client-operationsfrom
stack/database-stable/06-serve-operations
Open

ditadi wants to merge 5 commits 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?

Authored operations are now the only database HTTP surface. database() discovers the generated registry for server/database/routes/, so an app using the conventional layout no longer imports routes.gen.ts in its server entry.

  • database() discovers operations, database({ routes: false }) keeps server-side access only, and database({ routes: customRoutes }) uses an explicit registry.
  • The standard appkitServerConfig() build includes the registry, schema, and operation modules automatically. Production discovery loads the compiled registry beside the server entry, including custom output directories. Clean builds remove stale registries when sources are deleted.
  • Development setup regenerates the source registry before importing it. Native imports let tsx watch track routes, shared row schemas, and policies; first-route additions and last-route deletions reload correctly. Production uses the compiled registry without generating files.
  • Outside development, missing generation and malformed registries fail setup with actionable errors before opening a pool. Explicit routes and disabled routes bypass discovery.
  • 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 playground omits the list input schema and client argument, declares the delete ID schema explicitly, and uses skipToken for dependent note reads. Its row-answering operations declare explicit outputs without author_email, and notes.delete answers no body. Documentation explains omitted-input and omitted-output semantics, explicit schemas, that .private() does not filter an output, and that a policy's resource lookup receives actor.
  • Guidelines explain policy-resource snapshots, atomic transaction conditions and database constraints, database-generated identifiers, safe field-validation details, operation/session-scoped mutation state, and hooks for rendered React data.
  • The playground, template guidance, and documentation use automatic discovery. The playground writes a note and its event in one transaction, and only the author may edit or delete a note.

This replaces the beta database HTTP API. Operations without input schemas accept no parameters, and operations without output schemas answer no body.

Interface

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

await createApp({ plugins: [server(), database()] });

// Alternatives:
database({ routes: false });
database({ routes: customRoutes });
config/database/schema.ts                       schema declarations
server/database/routes/*.ts                     defineOperation exports
server/database/policies.ts                     shared policies outside routes/
server/database/routes.gen.ts                   generated, committed
shared/appkit-types/database-operations.d.ts     generated, committed

An explicit schema overrides conventional schema discovery. With an explicit routes registry, the schema must be the same object the registry loads. Custom server builds must include the registry entry beside their server using the same file extension, or import and pass the registry explicitly.

Removed without a compatibility layer:

  • database({ api }), its AutoCRUD configuration types, and routes under /api/database/<table>.
  • databaseApi.list, get, create, update, and remove; the entity CRUD hooks; and invalidateDatabaseReads.
  • The DatabricksAppKitDatabaseRegistry global that the entity hooks read.
  • The CRUD column codecs and table compiler (crud/codecs.ts, compileCrudTables) and the shared WireOutput type, which lost their last consumers.

Validation

  • After the review-feedback commit: 840 database and type-generator tests (including the tsx watch test below) and the playground smoke test (10) pass.
  • A real tsx watch + Vite test covers schema-only startup, first route creation, route edits, shared schema edits, added routes, deletions, and final route removal without a restart loop.
  • pnpm build, pnpm docs:build, pnpm check:fix, and pnpm -r typecheck pass.
  • Not rerun after the review-feedback commit: the playground client typecheck and production build, and the local AML scenario tests below.
  • The source-less production bundle check loads all three fixture operations under plain Node after removing TypeScript sources.
  • Application scenario validation is local only: 10 real operation/PostgreSQL integration tests and 5 screen tests pass. The disposable database uses a private Unix socket and no TCP listener. The AML app, its migrations, and its tests remain ignored and are not included in this or any other PR.

Live Databricks Apps identity and Lakebase were not exercised. The docs build reports an existing broken anchor in custom-plugin telemetry documentation; it completes successfully.

Stack

#683 → #687 → #688 → #689 → #690 → #691 → #692

Design: #683. Previous layer: #691.

Review feedback: refactor(appkit): migrate operations to explicit outputs migrates the playground, tests, and guide, and removes the orphaned codecs.


Stack created with GitHub Stacks CLI • Give Feedback 💬

@ditadi
ditadi added this pull request to stack #693 October 23, 2026 15:01
@github-actions

github-actions Bot commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor

📦 Bundle size report

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

@databricks/appkit

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

dist raw gzip
JS (runtime) 1.3 MB (+15 KB) 467 KB (+7.7 KB)
Type declarations 474 KB (+9.7 KB) 172 KB (+3.4 KB)
Source maps 2.6 MB (+41 KB) 876 KB (+17 KB)
Other 11 KB 3.7 KB
Total 4.3 MB (+66 KB) 1.5 MB (+28 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.3 KB) 2.5 KB (-1 B) 113 KB (+4.3 KB) external 357 KB (+12 KB)
./beta 105 KB (+1.9 KB) 4.5 KB (+4.0 KB) 109 KB (+5.9 KB) external 320 KB (+10 KB)
./testing 45 KB (+311 B) 36 KB (+4.0 KB) 82 KB (+4.3 KB) external 236 KB (+12 KB)
./tsdown 630 B (+110 B) 0 B 630 B (+110 B) external 1.0 KB (+233 B)
./type-generator 30 KB (+3.7 KB) 0 B 30 KB (+3.7 KB) external 85 KB (+11 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 79 KB
./beta stream-manager.js initial 5.9 KB
./beta define-schema.js initial 5.8 KB
./beta service-context.js initial 4.2 KB
./beta modular.js initial 3.5 KB
./beta databricks.js initial 3.3 KB
./beta wide-event-emitter.js initial 2.8 KB
./beta client.js initial 645 B
./beta index.js initial 20 B
./beta index.js lazy 4.0 KB
./beta supervisor-api.js lazy 201 B
./beta databricks.js lazy 185 B
./beta index.js lazy 125 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 630 B
./type-generator index.js initial 30 KB

@databricks/appkit-ui

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

dist raw gzip
JS (runtime) 416 KB (-13 KB) 141 KB (-5.6 KB)
Type declarations 244 KB (-23 KB) 90 KB (-8.5 KB)
Source maps 814 KB (-39 KB) 271 KB (-14 KB)
CSS 16 KB 3.2 KB
Total 1.5 MB (-75 KB) 506 KB (-28 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 (+277 B) 0 B 2.4 KB (+277 B) 0 B 5.2 KB (+200 B)
./react 433 KB 49 KB 481 KB 1.3 MB 177 KB
./react/beta 3.7 KB (-1.2 KB) 0 B 3.7 KB (-1.2 KB) 0 B 8.6 KB (-3.8 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.7 KB

@github-actions

github-actions Bot commented Oct 23, 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 42205550889 -R databricks/appkit -n appkit-template-0.90.0-pr.2398f3e-stack-database-stable-06-serve-operations-692 -D appkit-pr-692 \
  && unzip -o "appkit-pr-692/appkit-template-0.90.0-pr.2398f3e-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.

@ditadi
ditadi force-pushed the stack/database-stable/06-serve-operations branch from f72c9a7 to 8a9f21c Compare October 26, 2026 10:41
@ditadi
ditadi marked this pull request as ready for review October 28, 2026 11:05
@ditadi
ditadi requested a review from a team as a code owner October 28, 2026 11:05
@ditadi
ditadi requested a review from atilafassina October 28, 2026 11:05
@ditadi
ditadi force-pushed the stack/database-stable/06-serve-operations branch from 4b113fa to 2fd1455 Compare November 3, 2026 13:01
@ditadi
ditadi force-pushed the stack/database-stable/06-serve-operations branch from 2fd1455 to 30d4a43 Compare November 3, 2026 13:47
@ditadi
ditadi requested review from MarioCadenas and removed request for atilafassina November 3, 2026 13:55
@ditadi
ditadi removed this pull request from stack #693 November 3, 2026 15:18
@ditadi
ditadi force-pushed the stack/database-stable/06-serve-operations branch from 30d4a43 to a483f29 Compare November 3, 2026 15:18
@ditadi
ditadi added this pull request to stack #704 November 3, 2026 15:19
@ditadi
ditadi force-pushed the stack/database-stable/06-serve-operations branch from a483f29 to 4fb8105 Compare November 4, 2026 19:15
@ditadi
ditadi force-pushed the stack/database-stable/06-serve-operations branch from 4fb8105 to 2f36a19 Compare November 4, 2026 20:06
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 and others added 4 commits November 6, 2026 10:49
Signed-off-by: ditadi <victordperd@gmail.com>
Signed-off-by: ditadi <victordperd@gmail.com>
The plugin validates and serves operations without the schema's table set,
and the playground declares an output on boards.create, notes.create, and
notes.update while notes.delete answers no body. The database guide drops
the entity field and the default-output table, says that every callback,
the resource lookup included, receives the actor, and states that private()
does not sanitize an operation's output. The table contract and column
codecs that only default outputs used go with them, along with the shared
WireOutput type.

Co-authored-by: Isaac <no-reply@databricks.com>
Signed-off-by: ditadi <victordperd@gmail.com>
@ditadi
ditadi force-pushed the stack/database-stable/06-serve-operations branch from 2f36a19 to 5fef7c8 Compare November 6, 2026 10:49

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