Subgraph Composer — Specification & Design
Overview
The Subgraph Composer is a lightweight, browser-based utility that allows users to:
- Input 1–10 JSON Schemas with
x-graphqlextensions - Generate GraphQL subgraphs from each schema
- Compose subgraphs into a unified supergraph
- Visualize and explore the result using GraphQL Editor
Key Constraint: Minimize computational cost and bundle size by using the smallest possible code editor.
Architecture
Technology Stack
| Component | Technology | Reason |
|---|---|---|
| JSON Schema Editor | CodeMirror v6 + JSON mode | ~5KB gzipped, highly configurable |
| Converter | @json-schema-x-graphql/core (Node.js) | Already built, tested, supports federation |
| GraphQL Preview | graphql-editor (existing) | Already integrated, proven |
| UI Framework | React 18 | Consistent with project |
| Build Tool | Vite | Fast, lightweight, already used |
| Bundler Strategy | Code splitting + lazy loading | Defer CodeMirror/GraphQL Editor until needed |
Bundle Size Targets
Base app: ~50 KB
+ CodeMirror (lazy): ~15 KB (loaded on demand)
+ GraphQL Editor: ~200 KB (lazy, already separate app)
Total initial load: ~50 KB (scales well to 10 schemas)Component Architecture
1. SchemaManager Component
Manages the collection of JSON schemas with minimal UI footprint.
interface SchemaEntry {
id: string; // Unique ID for tracking
name: string; // User-friendly name (e.g., "User Service")
content: string; // Raw JSON Schema
lastModified: number; // Timestamp
error?: string; // Validation/conversion errors
isLoading?: boolean; // Conversion in progress
}
interface SchemaManagerState {
schemas: SchemaEntry[];
activeSchemaId: string | null;
generatedSubgraphs: Map<string, string>; // id -> GraphQL SDL
supergraphSDL: string;
compositionErrors: string[];
}Features:
- Add schema (up to 10)
- Remove schema
- Rename schema
- Duplicate schema
- Reorder schemas
- Clear all
2. SchemaEditor Component
Minimal JSON editor using CodeMirror v6.
interface SchemaEditorProps {
schema: SchemaEntry;
onUpdate: (content: string) => void;
onGenerate: () => void;
}Features:
- Syntax highlighting (JSON)
- Line numbers
- Auto-indent
- Minimal UI (no minimap, no breadcrumbs)
- Status indicator (dirty/saved/error)
- Quick buttons: Format, Validate, Generate
3. SubgraphGenerator Component
Handles conversion from JSON Schema → GraphQL SDL.
interface SubgraphGeneratorOptions {
includeDescriptions: boolean;
includeFederationDirectives: boolean;
federationVersion: "V1" | "V2" | "AUTO";
namingConvention: "PRESERVE" | "GRAPHQL_IDIOMATIC";
}
async function generateSubgraph(
jsonSchema: Record<string, any>,
options: SubgraphGeneratorOptions,
): Promise<{ sdl: string; warnings: string[] }>;Features:
- Uses
@json-schema-x-graphql/corelibrary - Batch generation for multiple schemas
- Error recovery (continue on single schema failure)
- Progress indication
- Validation of output SDL
4. SupergraphComposer Component
Merges multiple subgraphs into a unified supergraph.
interface CompositionOptions {
mergeStrategy: "union" | "extend"; // How to handle type conflicts
includeRootQuery: boolean; // Auto-create Query root
federationMode: boolean; // Enable federation features
}
async function composeSupergraph(
subgraphs: Map<string, string>, // id -> SDL
options: CompositionOptions,
): Promise<{ sdl: string; errors: string[]; stats: CompositionStats }>;
interface CompositionStats {
totalTypes: number;
totalFields: number;
mergedTypes: number;
conflicts: string[];
}Algorithm:
- Parse all subgraph SDLs
- Build type registry (detect conflicts)
- Merge type definitions (extend or union strategy)
- Create unified Query/Mutation roots
- Validate final SDL
5. SupergraphPreview Component
Displays and explores the generated supergraph.
interface SupergraphPreviewProps {
sdl: string;
errors: string[];
stats: CompositionStats;
onOpenInEditor: (sdl: string) => void;
}Features:
- Syntax-highlighted SDL display
- Type browser (show all types, search)
- Error display with line numbers
- Copy SDL button
- “Open in GraphQL Editor” button
- Stats summary
File Structure
frontend/subgraph-composer/
├── package.json
├── vite.config.js
├── index.html
├── src/
│ ├── main.jsx
│ ├── App.jsx
│ ├── App.css
│ ├── components/
│ │ ├── SchemaManager.jsx # Schema list, add/remove/rename
│ │ ├── SchemaEditor.jsx # CodeMirror editor
│ │ ├── SubgraphGenerator.jsx # Converter integration
│ │ ├── SupergraphComposer.jsx # Composition logic
│ │ ├── SupergraphPreview.jsx # Display results
│ │ └── ErrorBoundary.jsx # Error handling
│ ├── hooks/
│ │ ├── useSchemaManager.js # State management
│ │ ├── useSubgraphGenerator.js # Conversion wrapper
│ │ └── useComposition.js # Composition wrapper
│ ├── lib/
│ │ ├── converter.js # Wrapper around @json-schema-x-graphql/core
│ │ ├── composer.js # Supergraph composition logic
│ │ ├── validation.js # Schema/SDL validation
│ │ └── storage.js # LocalStorage persistence
│ └── styles/
│ ├── global.css
│ ├── manager.css
│ ├── editor.css
│ └── preview.css
└── tests/
├── converter.test.js
├── composer.test.js
└── integration.test.jsWorkflow
User Journey
1. User opens the app
└─ Empty schema list + "Add Schema" button
2. User adds first JSON Schema
└─ Paste JSON or use template → "Generate Subgraph"
3. App converts to GraphQL Subgraph
└─ Converter library produces SDL; shows warnings/errors
4. User adds more schemas (2–10)
└─ Auto-compose on each generation
5. App composes Supergraph
└─ Merges subgraphs, detects conflicts, shows unified schema
6. User views Supergraph in Preview
└─ Syntax-highlighted SDL, type browser, composition stats
7. User opens in GraphQL Editor
└─ Full exploration in graphql-editor (separate app)Performance Considerations
Bundle Size Optimization
-
Code Splitting:
- Core app: React + basic UI (~50 KB)
- CodeMirror: Lazy load on first schema edit (~15 KB)
- GraphQL Editor: Separate app, lazy load (~200 KB)
-
Converter Library:
- Use pre-compiled dist from
@json-schema-x-graphql/core - Tree-shake unused exports
- Use pre-compiled dist from
-
State Management:
- Use React hooks (no Redux)
- LocalStorage for persistence
- Debounce editor updates
Computational Efficiency
- Conversion: Single-threaded; process one schema at a time; show progress for 10+ schemas
- Composition: Simple graph merging; cache subgraph results; debounce after edits
- Memory: Limit schema size to 100 KB each; clear old compositions; release unused references
Feature Flags & Options
Converter Options Panel
□ Include Descriptions
□ Include Federation Directives
Federation Version: [AUTO ▼] (AUTO | V1 | V2)
Naming Convention: [GRAPHQL_IDIOMATIC ▼] (PRESERVE | GRAPHQL_IDIOMATIC)
□ Infer IDsComposition Options Panel
Merge Strategy: [EXTEND ▼] (UNION | EXTEND)
□ Create Root Query (auto)
□ Federation ModeSchema Import/Export
- Import: Paste JSON, drag & drop file, GitHub Gist URL, copy from existing schema
- Export: Download individual subgraph SDL, download supergraph SDL, export all (ZIP), copy to clipboard
API & Integration Points
Converter Integration
// lib/converter.js
import { jsonSchemaToGraphQL } from "@json-schema-x-graphql/core";
export async function convertSchema(jsonSchema, options = {}) {
try {
const sdl = jsonSchemaToGraphQL(jsonSchema, {
validate: true,
includeDescriptions: options.descriptions ?? true,
includeFederationDirectives: options.federation ?? true,
federationVersion: options.federationVersion ?? "AUTO",
namingConvention: options.naming ?? "GRAPHQL_IDIOMATIC",
});
return { sdl, success: true };
} catch (error) {
return { sdl: null, success: false, error: error.message };
}
}Composition Integration
// lib/composer.js
import { parse, buildSchema, printSchema } from "graphql";
export function composeSupergraph(subgraphs, options = {}) {
// 1. Parse all SDL strings
// 2. Extract type definitions
// 3. Merge based on options.mergeStrategy
// 4. Build unified Query root if needed
// 5. Return merged SDL
}GraphQL Editor Integration
// Navigate to graphql-editor with supergraph SDL:
window.open(`/graphql-editor?schema=${encodeURIComponent(supergraphSDL)}`);Testing Strategy
Unit Tests
converter.test.js: Schema → SDL conversionscomposer.test.js: Subgraph merging logicvalidation.test.js: Input validationstorage.test.js: LocalStorage persistence
Integration Tests
- End-to-end: 1 schema → subgraph → preview
- Multiple schemas → composition → preview
- Error handling: Invalid JSON, circular refs, conflicts
- Performance: 10 schemas, 100 KB each
Success Criteria
- ✅ Accepts 1–10 JSON Schemas with
x-graphqlextensions - ✅ Generates GraphQL subgraphs for each schema
- ✅ Composes subgraphs into unified supergraph
- ✅ Displays result in graphql-editor
- ✅ Bundle size < 100 KB initial load (CodeMirror lazy)
- ✅ No computational lag with 10 schemas
- ✅ Handles errors gracefully (partial failures)
- ✅ Allows schema management (add, edit, remove, reorder)
- ✅ Persists state to localStorage
- ✅ Fully documented with examples