feat(playground): add a DatabasePlugin example - #529
Open
ditadi wants to merge 1 commit into
Open
Conversation
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>
This was referenced Aug 10, 2026
ditadi
requested review from
MarioCadenas and
atilafassina
and removed request for
pkosiec
August 10, 2026 22:40
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stack
Each PR targets the one above it, so the diff shown here is only the delta on top of #528. Review in order.
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_emailis.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 (
server/index.ts)Registered conditionally on
LAKEBASE_ENDPOINT, the same waylakebase()already is in this file, so the playground still boots without a database configured.crudRoutesenablesboardsandnotesbut notnote_events: the audit trail is written by a hook and stays server-only, which is the exposure decision from #527 shown rather than explained.The
noteshooks use all three points:beforeCreateruns an agent to redact personal names from the body before the insert, so the unredacted text never reaches the table, and stamps the privateauthor_email. This is the intended shape of the hook API — a hook is server code and can call any other plugin. The comment in the file is explicit that it also holds the transaction open while the model answers, which is the trade being made.serializeships a 120-character preview on the list route and the whole body on the detail route.afterCreatewrites thenote_eventsrow inside the insert's own transaction, so the note and the event describing it commit together or not at all.Two hand-written routes (
server/index.ts)Deliberately showing what a generated route is not for: one aggregate via the tagged
sqltemplate (a generated read shapes rows, it does not group them), and one nested include reached through the typed client.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.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.
Verification
pnpm vitest run— 4166 passing, 1 skippedpnpm -r typecheck— clean across all packagespnpm run generate:types,pnpm run sync:template, andpnpm run docs:buildproduce no drift