Files
skills/docs/api-stack/api/api-graphql-ui-graphiql/src/html/render-shell.md
2026-05-29 08:20:56 +01:00

2.4 KiB

html/render-shell

(no submodules)

Purpose

Renders the GraphiQL HTML shell. The output embeds the configured GraphQL endpoint, subscription endpoint, theme, and editor passthroughs into a <script id="graphiql-config" type="application/json"> block — NOT inline JavaScript — which the bundled graphiql.js parses at boot. This split keeps script-src 'self' viable.

The shell is intentionally minimalist. The bundled bootstrap drives the explorer end-to-end (text area for the query, JSON pre for the response). Operators who want the full React-based GraphiQL distribution can swap out the asset payload on a fork.

Features

  • Single-call render — renderShell(options) returns the full document.
  • Defensive escaping — every caller-supplied string (default query, endpoints, theme, editor options blob) is HTML-escaped.
  • No external host references — every src / href points inside <uiEndpoint>/assets/.
  • Asset paths embed FNV-1a hashes for cache-busting.

Usage: Basic

import {
    parseGraphiqlUiOptions,
    renderShell,
} from "@suaveplan/api-graphql-ui-graphiql";

const html = renderShell(parseGraphiqlUiOptions({}));
// html starts with <!DOCTYPE html> and references /graphql/ui/assets/...

Usage: Advanced

import {
    parseGraphiqlUiOptions,
    renderShell,
} from "@suaveplan/api-graphql-ui-graphiql";

const html = renderShell(
    parseGraphiqlUiOptions({
        uiEndpoint: "/explorer",
        graphqlEndpoint: "/api/v2/gql",
        subscriptionEndpoint: "/api/v2/gql/ws",
        theme: "dark",
        defaultQuery: "{ viewer { id name } }",
        editorOptions: { lineWrapping: true, tabSize: 2 },
    })
);

API Reference

  • renderShell(options: GraphiqlUiOptions): string — produces the fully-substituted HTML document.
  • escapeHtml(input: string): string — replaces &, <, >, ", and ' with HTML entities.

Implementation Notes

The config block uses application/json so the bootstrap can call JSON.parse(node.textContent) without sniffing or eval. Escaping the JSON via escapeHtml is technically over-protective (JSON inside a <script type="application/json"> block doesn't need entity encoding of </>/& for syntax correctness, but it does need it for defense-in-depth when the JSON contains a </script> substring) — we err on the safe side and run every embedded string through escapeHtml.