4.1 KiB
@suaveplan/api-graphql-ui-graphiql — GraphiQL UI mount at /graphql/ui
Package: @suaveplan/api-graphql-ui-graphiql
Status: PROPOSED
Type: new-package
Stage: 7 (GraphQL + WebSocket / SSE engines) — see umbrella openspec/changes/api-plugin-foundation/
Why
Per design doc §10.6 the foundation reserves GET /graphql/ui for an interactive query explorer but does not bundle one by default — UI choice is opt-in via a separate package so the host's runtime image stays minimal for production deployments that don't need it. GraphiQL is the default option called out in §10.6 ("GET /graphql/ui — GraphiQL (or Altair via api-graphql-ui-altair)"). Operators wire this package when they want the canonical GraphQL UI; they wire api-graphql-ui-altair instead when they prefer Altair.
The GraphQL engine adapters (api-graphql-yoga, api-graphql-apollo) intentionally return 404 at /graphql/ui when no UI plugin is wired — so this package is the standard way to give developers an in-browser explorer.
What changes
Net-new package depending on @suaveplan/api-types, @suaveplan/api-router, @suaveplan/api-graphql, @suaveplan/error, @suaveplan/logger, and graphiql (peer; static assets served from this package, not fetched from a CDN).
- ADDED:
createGraphiqlUi(opts)— factory returning aRouterPluginvalue that registers aGET <uiEndpoint>handler serving the GraphiQL HTML shell + assets. - ADDED:
GraphiqlUiOptions— Zod-validated config:uiEndpoint(default/graphql/ui),graphqlEndpoint(default/graphql),subscriptionEndpoint(default/graphql/ws),theme("light"|"dark"|"system", default"system"),defaultQuery,defaultVariableEditorOpen,editorOptions(codemirror passthroughs). - ADDED: Static-asset serving — GraphiQL's bundle is served from the package's own
dist/assets/(no CDN dependency). The HTML shell embeds versioned hashes for cache-busting. - ADDED: CSP/security headers —
Content-Security-Policy: default-src 'self'; style-src 'self' 'unsafe-inline'(GraphiQL needs inline styles) emitted on the HTML response. - ADDED:
GRAPHQL_UI_GRAPHIQL_*error codes:GRAPHQL_UI_GRAPHIQL_ASSET_MISSING,GRAPHQL_UI_GRAPHIQL_ENDPOINT_COLLISION. - ADDED: OTel instruments:
graphql.ui.requests_total { ui: "graphiql" }counter.
Out of scope
- A CDN-fetched GraphiQL variant — bundled assets only, per the foundation's "no runtime CDN fetches" rule (security + offline-dev posture).
- The Altair alternative — separate package
api-graphql-ui-altair. - Schema introspection — that's the engine adapter's job (Yoga / Apollo configure introspection on
/graphql). This package just renders an explorer that calls into whatever the engine exposes. - Authentication on the UI route — host config decides whether
/graphql/uiis behind auth; this package surfaces aRouterPluginwhose route options can be configured by the operator.
Source mapping
Genesis-original. Source of design intent: docs/superpowers/specs/2026-05-13-api-plugin-foundation-design.md.
| Requirement | Design doc section |
|---|---|
GET /graphql/ui default to GraphiQL |
§10.6 |
| UI as opt-in via separate package | §10.6 + cross-ref to engine adapters returning 404 when not wired |
| Theme / config passthroughs | §10.6 (implied — interactive explorer) |
Verification approach
src/factory/create-graphiql-ui.test.ts— factory returns a validRouterPlugin; HTML response shape; CSP header presence; asset 200s.src/factory/create-graphiql-ui.spec-conformance.test.ts— pins that the mounted endpoint serves atuiEndpointand that the response embeds the configuredgraphqlEndpoint+subscriptionEndpoint.src/assets/assets.test.ts— every bundled asset reachable; missing-asset path raisesGRAPHQL_UI_GRAPHIQL_ASSET_MISSING.tests/integration/graphiql-end-to-end.test.ts(30000ms) — bootsapi-graphql-yoga+ this UI plugin, opens a Playwright browser at/graphql/ui, runs a query through the live editor, asserts results render.
Migration impact
None — additive only.