5.3 KiB
@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— abstractGraphqlRegistry:addTypes(sdl: string),addResolvers(map: ResolverMap),addLoader<K, V>(name: string, batcher: BatchFn<K, V>). - ADDED:
./resolver—Resolver<Parent, Args, Result, Caps>typed againstRequestContext<Caps>;ResolverMapkeyed by GraphQL type name. - ADDED:
./loader—LoaderFactory: per-request DataLoader instance creation; lifecycle wired to step 7 (capability scoping) of the pipeline. - ADDED:
./subscription—SubscriptionResolverreturningAsyncIterable; protocol marker forgraphql-ws(subscriptions rideapi-wsper §10.4). - ADDED:
./federation—mergeSdl(contributions: readonly SdlContribution[]): MergedSchema; collision detector →GRAPHQL.SCHEMA_COLLISIONwith the offending plugin pair + type/field path;graphql.namespacewrapper application. - ADDED:
./directives—@auth(scope)+@rateLimit(rpm)directive contracts (host-installed; engine adapters implement them). - ADDED:
./engine— abstractGraphqlEnginelifecycle interface (buildSchema,executeQuery,executeSubscription,dispose); concrete impls live in Stage 7 (api-graphql-yoga,api-graphql-apollo). - ADDED:
./capability-id— adds"graphql"to theCapabilityIdunion via the type-level merge with@suaveplan/api-types. - ADDED:
./error-codes— frozenGRAPHQL_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/addLoaderare accumulator-style; multiple calls compose; deduplication of identical contributions.src/federation/merge-sdl.test.ts— union ofextend type Queryfrom multiple plugins; collision detection.src/federation/namespace.test.ts—graphql.namespace: "Billing"wraps a plugin's contributions underBillingQuery/BillingMutation.src/subscription/subscription.spec-conformance.test.ts—SubscriptionResolverreturnsAsyncIterable<T>and ties toapi-wsSubscriptionBus.src/directives/directives.test.ts—@authand@rateLimitdirective descriptors carry the contract.src/engine/engine.spec-conformance.test.ts—GraphqlEnginelifecycle interface present.src/capability-id/capability-id.spec-conformance.test.ts—"graphql"is in theCapabilityIdunion 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)