# `@suaveplan/api-graphql-yoga` — GraphQL Yoga engine adapter (default engine) **Package**: `@suaveplan/api-graphql-yoga` **Status**: PROPOSED **Type**: new-package **Stage**: 7 (GraphQL + WebSocket / SSE engines) — see umbrella `openspec/changes/api-plugin-foundation/` ## Why GraphQL is a first-class engine alongside REST in the foundation (design doc §10). Plugins contribute types and resolvers via the abstract `api-graphql` Registry; one of the host's adapters federates a single merged schema across all plugins and dispatches requests. Per §10.5 GraphQL Yoga is the default engine — Bun-friendly, fastest for our usage, simpler operational story than Apollo Server. This package binds the abstract `api-graphql` contract to Yoga. Without this adapter the abstract `api-graphql` contract has no runnable implementation, and the umbrella's Stage-7 acceptance ("`api-plugin-example` mounts a federated GraphQL schema across two plugins AND publishes a tenant-scoped WebSocket broadcast") can't be reached. ## What changes Net-new package depending on `@suaveplan/api-types`, `@suaveplan/api-graphql` (abstract contract), `@suaveplan/api-router` (mounts HTTP endpoints), `@suaveplan/api-ws` (subscription transport), `@suaveplan/error`, `@suaveplan/logger`, `@suaveplan/opentelemetry`, `graphql` (peer), `graphql-yoga` (peer), and `zod`. - ADDED: `createYogaGraphqlEngine(opts)` — factory returning the engine implementing `@suaveplan/api-graphql`'s engine contract. - ADDED: `YogaGraphqlOptions` — Zod-validated config: `endpoint` (default `/graphql`), `sdlEndpoint` (default `/graphql/sdl`), `wsEndpoint` (default `/graphql/ws`), `uiEndpoint` (default `/graphql/ui`, only mounted if a UI plugin like `api-graphql-ui-graphiql` is wired), `maskedErrors`, `landingPage`, `maxQueryDepth`, `maxQueryComplexity`, `parserCacheSize`. - ADDED: Endpoint mounts: - `POST /graphql` — main execution endpoint. - `GET /graphql/sdl` — merged SDL across all federated plugin contributions. - `WS /graphql/ws` — `graphql-ws` protocol over the `api-ws` engine. - `GET /graphql/ui` — proxied to the wired UI plugin (this package does NOT mount GraphiQL/Altair itself; those are separate packages). - ADDED: Schema federation — wraps multiple plugin contributions into one merged schema via `@graphql-tools/schema` `makeExecutableSchema`. Collisions raise `GRAPHQL.SCHEMA_COLLISION` (PluginScope error code on `@suaveplan/api-graphql`'s namespace). - ADDED: Subscriptions — bridges `graphql-ws` over the `api-ws` engine. Subscription resolvers return `AsyncIterable` driven by the event bus. - ADDED: Per-request `DataLoader` instances scoped to `RequestContext`, populated at pipeline step 7 and disposed at step 11 (design doc §10.3). - ADDED: `GRAPHQL_YOGA_*` error codes: `GRAPHQL_YOGA_PARSE_FAILED`, `GRAPHQL_YOGA_VALIDATION_FAILED`, `GRAPHQL_YOGA_DEPTH_EXCEEDED`, `GRAPHQL_YOGA_COMPLEXITY_EXCEEDED`, `GRAPHQL_YOGA_SCHEMA_BUILD_FAILED`, `GRAPHQL_YOGA_SUBSCRIPTION_TRANSPORT_UNAVAILABLE`, `GRAPHQL_YOGA_TIMEOUT`, `GRAPHQL_YOGA_INTROSPECTION_DENIED`, `GRAPHQL_YOGA_PERSISTED_QUERY_NOT_FOUND`, `GRAPHQL_YOGA_RESOLVER_THREW`. - ADDED: OTel instruments: `graphql.requests_total { operation_type, operation_name, outcome }`, `graphql.request_duration_ms`, `graphql.resolver_duration_ms { type, field }`, `graphql.subscriptions_active` gauge, `graphql.schema_rebuilds_total`. - ADDED: `./testing` subpath — re-export of `runGraphqlEngineConformance` from `api-graphql/testing` bound to `createYogaGraphqlEngine`. ## Out of scope - The abstract `api-graphql` contract, schema-collision error code namespace, Registry / Resolver / LoaderFactory types — those live in `api-graphql` (Stage 1). - The GraphiQL or Altair UI — separate packages `api-graphql-ui-graphiql` and `api-graphql-ui-altair`. This adapter only proxies `/graphql/ui` to whichever is wired. - Cross-plugin federation transport (the merge itself happens on the host); this adapter just consumes the already-merged schema descriptor. - Apollo-specific features (persisted-query manifests, Apollo Studio) — that's `api-graphql-apollo`. ## Source mapping Genesis-original. Source of design intent: `docs/superpowers/specs/2026-05-13-api-plugin-foundation-design.md`. | Requirement | Design doc section | |---|---| | Default engine for GraphQL | §10.5 | | Endpoint set (POST /graphql, GET /graphql/sdl, WS /graphql/ws, GET /graphql/ui) | §10.6 | | Federation via `extend type Query { ... }` across plugins | §10.2 | | Subscriptions over `api-ws` via `graphql-ws` | §10.4 | | Per-request DataLoader scoping | §10.3 | | Same conformance suite as `api-graphql-apollo` | §10.5 (last sentence) | ## Verification approach - `src/engine/yoga-engine.test.ts` — schema build, request execution, error masking, depth/complexity guards. - `src/engine/yoga-engine.spec-conformance.test.ts` — runs `runGraphqlEngineConformance("yoga", createYogaGraphqlEngine)`; pins identical invariants to those required of `api-graphql-apollo`. - `src/federation/federation.test.ts` — two plugins each `extend type Query`; assert the merged schema serves both fields; assert collision raises `GRAPHQL.SCHEMA_COLLISION`. - `src/subscriptions/subscriptions.test.ts` (integration, 30000ms) — subscribes via `graphql-ws` over `api-ws-broker-in-memory`; broadcasts a tenant-scoped event; asserts delivery. - `src/loaders/loaders.test.ts` — per-request DataLoader instances; assert disposal after step 11. - `tests/integration/end-to-end.test.ts` (60000ms) — full pipeline: REST request → GraphQL request → subscription, all over Yoga, against an in-memory router + WS engine. ## Migration impact None — additive only.