Migrate hand-written GraphQL schema to Postgraphile v5#2
Merged
Conversation
beacon-api was the lone surveyed omni *-api hand-writing its schema via
graphql-yoga createSchema, against the golden rule (database-first via
Postgraphile). Move execution onto the Postgraphile/Grafast runtime:
- graphile.config.ts: Amber + simplify-inflection + connection-filter preset
- BeaconPlugin (extendSchema): ports the curated surface 1:1 — observer viewer,
Observer.{subscription,preferences,memories,memoriesSince}, and the
updatePreferences/createGatewaySession/pushMemories/deleteMemory/updateMemory
mutations — as Grafast lambda plans calling Drizzle, preserving the exact
LWW-merge, content-hash dedup, sync-cursor, and CloudEvents logic
- OmitTablesPlugin: omits underlying tables from auto-CRUD so the plugin owns
the curated types without collision (a follow-up may re-expose CRUD)
- server.ts: makeSchema(preset) at boot + useGrafast(); context injects
observer/db/withPgClient into the Grafast context
- remove hand-written src/lib/graphql/schema.ts
Verified statically: bun install, tsc --noEmit, biome check, knip all pass.
NOT runtime-verified: makeSchema/resolver execution needs a live Postgres +
IDP auth; sync/merge parity must be DB-tested before merge.
DB-backed verification (against a real Postgres) surfaced two issues a static
check could not, plus confirmed the migration now works end-to-end:
- rename data type `Subscription` -> `BillingSubscription` (the bare name
collides with GraphQL's reserved root subscription type; the
`observer.subscription` field is unchanged). Minor client codegen impact.
- OmitTablesPlugin: use `behavior: "-*"` instead of `omit: true` so the
underlying tables generate no type at all (v5 `omit` only drops operations),
removing Memory/UserPreferences/etc. collisions.
- commit generated schema.graphql (matches the prior hand-written surface).
Verified: makeSchema builds; server boots; `{ observer }` resolves null when
unauthenticated; all five mutations present. Authenticated mutation parity
(pushMemories LWW etc.) still warrants DB-backed tests, but the logic is a
verbatim port of the prior resolvers.
Contributor
Author
✅ Full DB-backed parity verification passedRan an end-to-end test against a real Postgres 18 with a HIDRA-style RS256 JWT (local JWKS), exercising the authenticated logic the migration was waiting on:
Combined with the earlier Note: merging to |
- knip: enable `-knipignore` tag handling so the existing @knipignore annotations on the boot-time flags provider and the public TokenPayload type are honored, and register test files as entries - test: add unit coverage for extractBearerToken (the auth header boundary), which also satisfies bun test's no-tests-found exit code Lint, tsc, knip, and bun test all pass locally.
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 subscribe to this conversation on GitHub.
Already have an account?
Sign in.
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.
Summary
Migrates beacon-api off its hand-written
graphql-yogacreateSchemaonto the omni-standard database-first Postgraphile v5 runtime, preserving the exact public API and sync semantics.graphile.config.ts— Amber + simplify-inflection + connection-filter presetBeaconPlugin(extendSchema) — ports the curated surface 1:1 (observerviewer;Observer.{subscription,preferences,memories,memoriesSince};updatePreferences/createGatewaySession/pushMemories/deleteMemory/updateMemory) as Grafastlambdaplans calling Drizzle, keeping the exact LWW-merge, content-hash dedup, sync-cursor, and CloudEvents logicOmitTablesPlugin— omits the underlying tables from auto-CRUD so the plugin owns the curated types without name collisions (follow-up may re-expose CRUD with connection filters)server.ts—makeSchema(preset)at boot +useGrafast(); context injectsobserver/db/withPgClientsrc/lib/graphql/schema.tsVerification
✅ Static:
bun install,tsc --noEmit,biome check,knipall pass.makeSchema/resolver execution needs a live Postgres + IDP auth. Before merge, run against a test DB and confirm parity for:observer(null when unauth; IDP find-or-create)pushMemoriesLWW merge (insert/update/duplicate counts,accessCountmax-merge, tombstones) +beacon.memories.syncedeventmemoriesSincepage-size/cursor/hasMore+sync_cursorsupsertdeleteMemorysoft-delete +updateMemorypin, with eventsplan/statusupper-cased)Design notes:
~/projects/omni/plans/2026-06-24-beacon-api-postgraphile-migration.md🤖 Generated with Claude Code