Skip to content

fix(graphql-server): resolve hosts in the resolver schema routing_planes() names - #1932

Merged
pyramation merged 2 commits into
mainfrom
feat/routing-resolver-schema
Oct 24, 2026
Merged

pyramation merged 2 commits into
mainfrom
feat/routing-resolver-schema

Conversation

@pyramation

@pyramation pyramation commented Oct 24, 2026 •

Copy link
Copy Markdown
Contributor

Summary

The server used to call "routing_public".resolve_route(...) directly, and called each tenant plane's resolver in that plane's API schema. Now it reads the resolver's schema from the database (function_resolution.routing_planes() → resolver_schema_name), so the resolver can live in a different schema from the plane's API schema without any server change.

  • loadDatabaseRoutingPlanes → loadRoutingPlanes returns every plane as { databaseId, schema, resolverSchema, resolver }.
  • resolveRouteAcrossPlanes takes the server's own plane from that list (schema === ROUTING_SCHEMA), resolves on it first, then on the tenant planes, always via quoteQualifiedIdentifier(plane.resolverSchema, plane.resolver). Nothing falls back to routing_public.
  • If a plane is missing its public schema, resolver schema or resolver function, the server throws ROUTING_PLANE_INCOMPLETE.
  • Fixtures: pgpm.json pins @pgpm/* to 0.47.7, the first release whose routing_planes() returns resolver_schema_name. The shared scoped seed and the tenant-routing-planes seed now register the server's own routing_public plane with a route_module row, the same way real deployments do. Without that row the server has no own plane to resolve on.

Tests: routing.test.ts covers a tenant-plane call through its resolver schema and the missing-resolver-schema throw. graphql/server passes 260/260 and graphql/server-test 232/232 locally.

Rollout coupling

  • Merge this before the database release that changes resolver_schema_name to a schema other than the API schema. The current published modules return the same schema for both, so this PR works against them as-is.
  • Once this is published, the database repo bumps its server image pin. Don't roll out that database change with an older server: an older server ignores resolver_schema_name and keeps calling routing_public.resolve_route.

Link to Devin session: https://app.devin.ai/sessions/ea7371d8dbc441c49cb46e16d1d97517
Open in Devin Desktop: https://app.devin.ai/desktop/session/ea7371d8dbc441c49cb46e16d1d97517?variant=devin
Requested by: @pyramation

@devin-ai-integration

Copy link
Copy Markdown
Contributor

I'll fix CI failures and address comments from users with write access. I'll skip comments containing "(aside)".

  • Disable automatic comment, CI, and merge conflict monitoring

@tenki-reviewer

tenki-reviewer Bot commented Oct 24, 2026 •

Copy link
Copy Markdown

Review complete. 🟡 1 medium

💬 Inline comments (1)

  • 🟡 Own routing_public plane now resolved only if routing_planes() lists it — routing.ts:212

The change replaces the previous hardcoded own-plane-first lookup in resolveRouteAcrossPlanes with a data-driven approach: the server's own routing_public plane is now identified by a name match against rows returned by function_resolution.routing_planes(), and tenant planes are loaded and iterated for host resolution. The routing test suite is updated to cover the new plane iteration behavior.

Files Change
graphql/server/src/middleware/routing.ts Rewrites resolveRouteAcrossPlanes to load tenant planes from routing_planes() and select the own plane by name match instead of hardcoding routing_public.
graphql/server/src/middleware/__tests__/routing.test.ts Updates routing middleware tests for the new multi-plane resolution flow.

Reviewed commit: 65ada1f

@tenki-reviewer tenki-reviewer Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR reworks multi-tenant route resolution in the GraphQL server middleware so host lookup runs across routing planes sourced from the function_resolution.routing_planes() table, with matching test coverage added.

Key findings

  • 🟡 Own routing_public plane now resolved only if routing_planes() lists it — routing.ts:212

Comment on lines +212 to +217
const planes = await loadRoutingPlanes(pool);
const own = planes.find((plane) => plane.schema === ROUTING_SCHEMA);
if (own) {
const route = await resolveRoute(pool, own.resolverSchema, host);
if (route) return { route, planeDatabaseId: null };
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 bug · medium

Own routing_public plane now resolved only if routing_planes() lists it

resolveRouteAcrossPlanes now resolves the server's own plane only when function_resolution.routing_planes() returns a row with schema === ROUTING_SCHEMA, and runs loadRoutingPlanes before any own-plane attempt (graphql/server/src/middleware/routing.ts:212-217). Previously resolveRoute(pool, ROUTING_SCHEMA, host) ran unconditionally first, so tenant-plane listing errors or incomplete registrations could not affect hosts owned by the server's own plane. Now a missing own-plane row silently skips own resolution (hosts fall through to tenant planes or 404), an incomplete tenant row throws ROUTING_PLANE_INCOMPLETE before own hosts are tried, and a failure of the routing_planes() query itself turns previously successful routing_public matches into errors.

📋 Prompt for AI Agents

In graphql/server/src/middleware/routing.ts in resolveRouteAcrossPlanes (around lines 208-229), restore the pre-change ordering: call resolveRoute(pool, ROUTING_SCHEMA, host) unconditionally first and return its match with planeDatabaseId: null before calling loadRoutingPlanes. In the tenant loop, skip planes whose schema === ROUTING_SCHEMA by value instead of relying on the own reference. This keeps the server's own plane resolving its hosts even when routing_planes() omits the platform registration, contains an incomplete tenant row, or errors.

@blacksmith-sh

This comment has been minimized.

…_name) and register the server's own routing_public plane
@pyramation
pyramation merged commit 4772b9d into main Oct 24, 2026
19 checks passed
@pyramation
pyramation deleted the feat/routing-resolver-schema branch October 24, 2026 21:39
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