Files
2026-06-09 21:06:27 +01:00

5.9 KiB

@suaveplan/api-graphql-ui-graphiql — GraphiQL UI mount for the Suaveplan API plugin system. Exposes createGraphiqlUi() — a factory returning a RouterPlugin that registers GET /graphql/ui (or any configured endpoint) and serves a self-contained HTML shell + bundled assets with a strict CSP. Default GraphQL explorer when operators want GraphiQL; sibling api-graphql-ui-altair covers the Altair alternative.

GraphiQL UI mount for the Suaveplan API plugin system. Stage 7 of the umbrella plan.

createGraphiqlUi(opts) returns a RouterPlugin that registers GET /graphql/ui (or any configured endpoint) on a host @suaveplan/api-router Router. The endpoint serves a self-contained HTML shell plus bundled JS/CSS assets — no CDN fetches.

Documentation tree

  • assets — Bundled static-asset registry for the GraphiQL UI plugin. The HTML shell references two assets (graphiql.js, `graphiq…
    • assets — Bundled static-asset registry for the GraphiQL UI plugin. The HTML shell references two assets (graphiql.js, `graphiq…
  • error-codes — Owns the stable GRAPHQL_UI_GRAPHIQL.* error-code registry consumed by the GraphiQL UI plugin. Every code in the regis…
    • error-codes — Owns the stable GRAPHQL_UI_GRAPHIQL.* error-code registry consumed by the GraphiQL UI plugin. Every code in the regis…
  • factory — Public entry point of the package. createGraphiqlUi(opts) returns a RouterPlugin — a plain value with name, `vers…
    • create-graphiql-ui — Public entry point of the package. createGraphiqlUi(opts) returns a RouterPlugin — a plain value with name, `vers…
  • headers — Owns the Content-Security-Policy header values emitted on the GraphiQL HTML response. The policy is hard-coded to a p…
    • csp — Owns the Content-Security-Policy header values emitted on the GraphiQL HTML response. The policy is hard-coded to a p…
  • html — Renders the GraphiQL HTML shell. The output embeds the configured GraphQL endpoint, subscription endpoint, theme, and e…
    • render-shell — Renders the GraphiQL HTML shell. The output embeds the configured GraphQL endpoint, subscription endpoint, theme, and e…
  • instruments — Owns the OpenTelemetry instrument bundle emitted by the GraphiQL UI plugin. A single Counter (`graphql.ui.requests_to…
    • instruments — Owns the OpenTelemetry instrument bundle emitted by the GraphiQL UI plugin. A single Counter (`graphql.ui.requests_to…
  • options — Zod-validated configuration surface for createGraphiqlUi. Owns GraphiqlUiOptions (resolved shape with every default…
    • options — Zod-validated configuration surface for createGraphiqlUi. Owns GraphiqlUiOptions (resolved shape with every default…

Why opt-in

Per the design doc (docs/superpowers/specs/2026-05-13-api-plugin-foundation-design.md §10.6) the API foundation reserves GET /graphql/ui for an interactive explorer but does not bundle one by default — UI choice is operator- chosen so the runtime image stays minimal for production deployments that don't need it.

Operators wire ONE of:

  • @suaveplan/api-graphql-ui-graphiql — canonical GraphiQL (this package).
  • @suaveplan/api-graphql-ui-altair — Altair alternative.

The GraphQL engine adapters (api-graphql-yoga, api-graphql-apollo) return 404 at /graphql/ui when no UI plugin is wired.

Quick start

import { Router } from "@suaveplan/api-router";
import { createGraphiqlUi } from "@suaveplan/api-graphql-ui-graphiql";

const router = new Router({ plugin: "host" });
createGraphiqlUi({}).register(router);
// GET /graphql/ui -> the explorer
// GET /graphql/ui/assets/graphiql.<hash>.js -> bundled JS
// GET /graphql/ui/assets/graphiql.<hash>.css -> bundled CSS

Configuration

Option Default Notes
uiEndpoint /graphql/ui Must start with /.
graphqlEndpoint /graphql Embedded into the explorer config.
subscriptionEndpoint /graphql/ws Embedded into the explorer config.
theme "system" "light" / "dark" / "system".
defaultQuery "" HTML-escaped before embedding.
defaultVariableEditorOpen false Opens the variables pane on load.
editorOptions {} CodeMirror passthroughs.
meterProvider OTel global OTel MeterProvider for the counter.

Security posture

  • Bundled assets only. Every src / href in the rendered HTML points inside the package's own /<uiEndpoint>/assets/ namespace. Cross-checked by src/html/render-shell.test.ts.
  • Strict CSP. default-src 'self'; style-src 'self' 'unsafe-inline'; script-src 'self'; img-src 'self' data:; connect-src 'self'. Inline scripts are NOT permitted. Cross-checked by src/headers/csp.test.ts.
  • Cache-immutable assets. Bundled JS/CSS carries Cache-Control: public, max-age=31536000, immutable.
  • Stale-hash tolerance. Hashed filenames resolve any matching stem+ext to the current asset bytes — rolling deploys don't 404 in-flight sessions.

OTel

  • graphql.ui.requests_total { ui: "graphiql" } — counter incremented on each successful HTML response. Asset hits are NOT counted.

Error codes

  • GRAPHQL_UI_GRAPHIQL.ASSET_MISSING — asset lookup failure.
  • GRAPHQL_UI_GRAPHIQL.ENDPOINT_COLLISION — <uiEndpoint> already registered by another plugin.

Tests

bunx --bun turbo lint typecheck test:coverage --filter=@suaveplan/api-graphql-ui-graphiql

100% statements / branches / functions / lines required on touched modules (provider: istanbul).