# `@suaveplan/api-graphql` — abstract GraphQL registry, resolver, loader, subscription contract **Package**: `@suaveplan/api-graphql` **Status**: PROPOSED **Type**: new-package **Stage**: 1 (Contracts) — see umbrella `openspec/changes/api-plugin-foundation/` ## Why GraphQL is a first-class engine alongside REST (design §10). Plugins mount GraphQL types + resolvers; the host federates a single schema across all plugins. Concrete engines (`api-graphql-yoga` default, `api-graphql-apollo` alternative) plug behind a stable contract so swapping engines is host-config only (design §10.5). This package owns that contract: the `Registry` (`addTypes` / `addResolvers` / `addLoader`), the `Resolver` shape, the `LoaderFactory` for DataLoaders, the `SubscriptionResolver` for GraphQL subscriptions over the WebSocket engine, and the federation rules (schema collision → `GRAPHQL.SCHEMA_COLLISION`, optional `graphql.namespace` wrapper). `graphql` is added as a new `CapabilityId` — plugins request it via `requiredCapabilities` (design §10.1, §5.2). ## What changes Net-new package. Pure contract — no concrete engine code. Some runtime: the federation merger (engine-agnostic SDL union with collision detection), Zod schemas for the subscription protocol, and the conformance harness. - ADDED: `./registry` — abstract `GraphqlRegistry`: `addTypes(sdl: string)`, `addResolvers(map: ResolverMap)`, `addLoader(name: string, batcher: BatchFn)`. - ADDED: `./resolver` — `Resolver` typed against `RequestContext`; `ResolverMap` keyed by GraphQL type name. - ADDED: `./loader` — `LoaderFactory`: per-request DataLoader instance creation; lifecycle wired to step 7 (capability scoping) of the pipeline. - ADDED: `./subscription` — `SubscriptionResolver` returning `AsyncIterable`; protocol marker for `graphql-ws` (subscriptions ride `api-ws` per §10.4). - ADDED: `./federation` — `mergeSdl(contributions: readonly SdlContribution[]): MergedSchema`; collision detector → `GRAPHQL.SCHEMA_COLLISION` with the offending plugin pair + type/field path; `graphql.namespace` wrapper application. - ADDED: `./directives` — `@auth(scope)` + `@rateLimit(rpm)` directive contracts (host-installed; engine adapters implement them). - ADDED: `./engine` — abstract `GraphqlEngine` lifecycle interface (`buildSchema`, `executeQuery`, `executeSubscription`, `dispose`); concrete impls live in Stage 7 (`api-graphql-yoga`, `api-graphql-apollo`). - ADDED: `./capability-id` — adds `"graphql"` to the `CapabilityId` union via the type-level merge with `@suaveplan/api-types`. - ADDED: `./error-codes` — frozen `GRAPHQL_CODES`: `SCHEMA_COLLISION`, `RESOLVER_MISSING`, `LOADER_NOT_REGISTERED`, `DIRECTIVE_UNKNOWN`, `SUBSCRIPTION_TRANSPORT_UNAVAILABLE`, `NAMESPACE_REQUIRED`. - ADDED: `./testing` — `runGraphqlEngineConformance(factory)` reusable harness. ## CapabilityId additions This package contributes `"graphql"` to the `CapabilityId` union. Pinned in the spec; documented here so reviewers can compare against the union maintained by `api-types`. ## Out of scope - Concrete engines (`api-graphql-yoga`, `api-graphql-apollo`) — Stage 7. - GraphiQL / Altair UIs — Stage 7 (`api-graphql-ui-graphiql`, `api-graphql-ui-altair`). - Persisted queries, Apollo Studio integration — engine-specific, Stage 7. ## Source mapping Genesis-original. Source of design intent: `docs/superpowers/specs/2026-05-13-api-plugin-foundation-design.md`. | Requirement | Design doc section | |---|---| | `Registry.addTypes` / `addResolvers` / `addLoader` shape | §10.1 | | Federation (`extend type Query` union; cross-plugin refs) | §10.2 | | Pipeline integration (`@auth`, `@rateLimit` directives) | §10.3 | | Subscriptions ride `api-ws` | §10.4 | | Engine choice (Yoga default, Apollo alternative) | §10.5 | | Collision → `GRAPHQL.SCHEMA_COLLISION` | §10.2 | | `graphql.namespace` wrapper | §10.2 | ### Requirements with no source line | Requirement | Justification | |---|---| | `runGraphqlEngineConformance` harness | Stage-5 conformance suite (§13.4) needs a contract-side signature. | ## Verification approach - `src/registry/registry.test.ts` — `addTypes`/`addResolvers`/`addLoader` are accumulator-style; multiple calls compose; deduplication of identical contributions. - `src/federation/merge-sdl.test.ts` — union of `extend type Query` from multiple plugins; collision detection. - `src/federation/namespace.test.ts` — `graphql.namespace: "Billing"` wraps a plugin's contributions under `BillingQuery`/`BillingMutation`. - `src/subscription/subscription.spec-conformance.test.ts` — `SubscriptionResolver` returns `AsyncIterable` and ties to `api-ws` `SubscriptionBus`. - `src/directives/directives.test.ts` — `@auth` and `@rateLimit` directive descriptors carry the contract. - `src/engine/engine.spec-conformance.test.ts` — `GraphqlEngine` lifecycle interface present. - `src/capability-id/capability-id.spec-conformance.test.ts` — `"graphql"` is in the `CapabilityId` union after augmentation. - `tests/dep-graph.test.ts` — pins deps: `@suaveplan/api-types`, `@suaveplan/error`, `@suaveplan/types`, `zod`. No GraphQL engine deps. ## Migration impact None — additive only. ## Impact ### Affected dependents - (none) — [x] no action needed (reason: net-new package; concrete engines land in Stage 7)