@graphql-workbench/schema-explorer
v0.3.0
Published
A macOS Finder-style column view web component for exploring GraphQL schemas
Maintainers
Readme
Schema Explorer
<gwb-schema-explorer> is a framework-agnostic web component for exploring GraphQL schemas
in a macOS Finder-style column view. It supports SDL and introspection inputs, global
search, semantic schema diffs, directives, deprecations, canonical navigation paths,
keyboard navigation, and light/dark theming.
Install
npm install @graphql-workbench/schema-explorer graphql @preact/signals-coregraphql and @preact/signals-core are peer dependencies. Lit is installed as a
normal runtime dependency.
Module usage
<gwb-schema-explorer id="explorer"></gwb-schema-explorer>
<script type="module">
import '@graphql-workbench/schema-explorer';
const explorer = document.querySelector('#explorer');
explorer.schema = `
type Query {
user(id: ID!): User
}
type User {
id: ID!
name: String!
}
`;
</script>The ES and CommonJS entries also export buildSchemaModel(), parseSDL(),
parseIntrospection(), diffSchemaModels(), createSchemaExplorerStore(),
SchemaExplorer, and the public TypeScript model, diff, selection, store, input, and
event-detail types. CommonJS is intended for browser-capable, ESM-aware bundlers;
importing the component requires a DOM.
For Node, SSR setup, or a coordinator that needs only semantic state, import
createSchemaExplorerStore() and parseSchemaExplorerStoreSnapshot() from
@graphql-workbench/schema-explorer/store. That DOM-free subpath has its own ES, CommonJS, and declaration
entries and does not register a custom element.
Signal store
createSchemaExplorerStore() exposes reusable semantic state without including column,
focus, scrolling, search-presentation, or copy UI state. Writable signals hold schema
inputs and preferences. Readonly signals expose resolved models, parse errors, diffs,
and the validated selection.
import { createSchemaExplorerStore } from '@graphql-workbench/schema-explorer';
const store = createSchemaExplorerStore();
store.setSchemaInput({
kind: 'raw',
format: 'sdl',
value: 'type Query { user: User } type User { id: ID! }',
});
const unsubscribe = store.events.subscribe('selection-change', ({ path, kind }) => {
console.log(path, kind);
});
store.select('field:Query.user');
console.log(store.model.value, store.selectionPath.value, store.diffChanges.value);
unsubscribe();
store.dispose();Current and baseline inputs use the same precedence as the component. The
setSchemaInput() and setBaselineSchemaInput() actions atomically replace all input
sources; consumers that need fallback behavior can write the individual signals.
Selection changes emit typed events even when input changes invalidate or restore the
selection. The include-deprecated-change preference event is action-only, so direct
preference writes do not echo. Pass { emitEvents: false } to an action when applying an
already-observed change. A coordinator may supply a shared writable theme signal through
{ theme }.
Use store.snapshot() for persistence and store.restore(value) to restore a snapshot.
Snapshots are versioned, detached, deeply frozen, and JSON-serializable. They include
all current and baseline inputs, the selection path, and durable preferences, but omit
derived models, parse errors, diffs, and presentation state. Restoration validates the
complete value before changing the store and does not emit user-action events.
parseSchemaExplorerStoreSnapshot(value) performs the same validation without mutating
a store, allowing a coordinator to validate all slices before restoring any of them.
Assign the same store to one or more components to synchronize their schema, preferences, diff, and semantic selection:
const store = createSchemaExplorerStore();
document.querySelector('#primary').store = store;
document.querySelector('#secondary').store = store;
store.schema.value = 'type Query { hello: String }';The existing component properties remain available and delegate to the active store.
When no store is supplied, the component owns a private one. Component interactions and
navigateTo() continue to emit the existing DOM events; changes made directly through
the store update every bound view without echoing DOM events. Disconnecting a view never
disposes an injected store. Clearing or replacing an injected store preserves its
durable inputs, preferences, and selection in a fresh private fallback.
The consumer that created an injected store calls dispose() after its last view is
finished; a view never disposes a borrowed store. The coordinator package
@graphql-workbench/graphql-workbench-store can own this slice alongside an execution slice. Schema
selection remains navigation state and never changes the consumer's query editor.
Inputs
The current schema uses this precedence order:
schemaModel: an immutable model returned by a parser orbuildSchemaModel()introspection: a bare introspection result or its{ data }HTTP envelopeschemawithformat="sdl"orformat="introspection"
Set the parallel baselineSchemaModel, baselineIntrospection, baselineSchema, and
baselineFormat inputs to compare a previous schema with the current schema. Clearing
every baseline input returns to ordinary exploration.
import { parseSDL } from '@graphql-workbench/schema-explorer';
explorer.schemaModel = parseSDL(currentSDL);
explorer.baselineSchema = previousSDL;Model default values are GraphQL literals. SDL parsing preserves the declared literal,
and introspection parsing preserves its supplied default-value text, including object
and list defaults for custom scalars. A programmatic schema default that cannot be
rendered and has no source literal appears as null without invalidating the schema.
explorer.diffChanges exposes the immutable semantic result. Changes are classified as
breaking, dangerous, or additive and can be opened from the built-in filtered
change list. If the baseline cannot be parsed, its comparison error appears above the
current schema; the current schema remains navigable. Correct the baseline to restore
the diff summary, or clear it to return to ordinary exploration.
Selection and events
selectionPath is a reflected, serializable path such as type:User,
field:Query.user, or argument:Query.user.id. Use await explorer.navigateTo(path)
to reveal and focus a destination.
The component emits bubbling, composed events:
gwb-selection-changewith{ path, kind }gwb-selection-path-errorwith{ path, reason }gwb-include-deprecated-changewith{ includeDeprecated }
Search, deprecation filtering, copy actions, and schema-change navigation are built into the component UI.
Themes and layout
Set theme to auto, light, or dark, and view-mode to multi-column or
single-column.
gwb-schema-explorer {
--gwb-se-height: 600px;
--gwb-se-column-width: 280px;
--gwb-se-font-family: Inter, sans-serif;
--gwb-se-font-size: 14px;
--gwb-se-selected-bg: #0058d0;
}All supported color, typography, layout tokens, and ::part() names are inventoried in
the project guidance.
The default palette meets WCAG AA contrast requirements; custom themes remain the
consumer's responsibility.
Standalone browser bundle
The self-contained UMD file bundles Lit and GraphQL for direct classic-script use:
<script src="schema-explorer.umd.js"></script>
<gwb-schema-explorer id="explorer"></gwb-schema-explorer>
<script>
explorer.schemaModel = SchemaExplorer.parseSDL('type Query { hello: String }');
</script>The SchemaExplorer global exposes the component, createSchemaExplorerStore(),
parseSDL(), parseIntrospection(), and diffSchemaModels(). It intentionally omits
buildSchemaModel(); consumers that already own a GraphQLSchema should use a module
entry so both sides share the same GraphQL runtime.
See MIGRATING.md for breaking changes and
CHANGELOG.md for release notes.
Development
npm ci
npx playwright install chromium firefox webkit
npm run check --workspace=@graphql-workbench/schema-explorerRun these commands from the monorepo root.
The unified check runs formatting, linting, TypeScript, Vitest, packed-package and bundle validation, plus Playwright in Chromium, Firefox, and WebKit. Manual assistive-technology checks remain in the release checklist.
