5.0 KiB
@suaveplan/api-graphql-apollo — Apollo Server GraphQL engine adapter
Package: @suaveplan/api-graphql-apollo
Status: PROPOSED
Type: new-package
Stage: 7 (GraphQL + WebSocket / SSE engines) — see umbrella openspec/changes/api-plugin-foundation/
Why
Per design doc §10.5 the foundation ships GraphQL Yoga as the default engine but offers Apollo Server as an alternative for shops standardised on Apollo's broader feature set — Apollo Studio integration, persisted-query manifest support, the @apollo/federation tooling, and a larger plugin ecosystem (including operation-cost analyzers and request-coalescing). Both engines MUST be host-config swappable, which is only meaningful if both implement the same api-graphql contract and pass the same conformance suite.
Without this adapter the foundation has only one GraphQL engine, undermining the §10.5 "engine choice" guarantee and forcing Apollo-standardised teams to fork the foundation.
What changes
Net-new package depending on @suaveplan/api-types, @suaveplan/api-graphql, @suaveplan/api-router, @suaveplan/api-ws, @suaveplan/error, @suaveplan/logger, @suaveplan/opentelemetry, @apollo/server (peer), graphql (peer), @apollo/server-plugin-landing-page-disabled (peer), and zod.
- ADDED:
createApolloGraphqlEngine(opts)— factory returning the engine implementing@suaveplan/api-graphql's engine contract. - ADDED:
ApolloGraphqlOptions— Zod-validated config:endpoint(default/graphql),sdlEndpoint(default/graphql/sdl),wsEndpoint(default/graphql/ws),uiEndpoint(default/graphql/ui),introspection(defaultfalse),persistedQueriesPath,studioReporting,maxQueryDepth,maxQueryComplexity,formatError. - ADDED: Endpoint mounts (identical to Yoga):
POST /graphql,GET /graphql/sdl,WS /graphql/ws,GET /graphql/ui(proxied to wired UI plugin). - ADDED: Apollo-specific features behind config flags:
- Persisted-query manifest (
persistedQueriesPath). - Apollo Studio usage reporting (
studioReporting). - Landing-page disabled by default (no GraphiQL fallback — same rule as Yoga: UI is opt-in via separate package).
- Persisted-query manifest (
- ADDED: Subscriptions — same
graphql-wsoverapi-wsbridge as Yoga. - ADDED: Per-request DataLoader scoping (identical to Yoga).
- ADDED:
GRAPHQL_APOLLO_*error codes:GRAPHQL_APOLLO_PARSE_FAILED,GRAPHQL_APOLLO_VALIDATION_FAILED,GRAPHQL_APOLLO_DEPTH_EXCEEDED,GRAPHQL_APOLLO_COMPLEXITY_EXCEEDED,GRAPHQL_APOLLO_SCHEMA_BUILD_FAILED,GRAPHQL_APOLLO_SUBSCRIPTION_TRANSPORT_UNAVAILABLE,GRAPHQL_APOLLO_TIMEOUT,GRAPHQL_APOLLO_INTROSPECTION_DENIED,GRAPHQL_APOLLO_PERSISTED_QUERY_NOT_FOUND,GRAPHQL_APOLLO_RESOLVER_THREW. - ADDED: OTel instruments: same surface as Yoga (
graphql.requests_total,graphql.request_duration_ms,graphql.resolver_duration_ms,graphql.subscriptions_active,graphql.schema_rebuilds_total) — the metric names are deliberately engine-agnostic so dashboards survive an engine swap. - ADDED:
./testingsubpath — re-export ofrunGraphqlEngineConformancebound tocreateApolloGraphqlEngine.
Out of scope
- The abstract
api-graphqlcontract. - Apollo Federation (subgraph composition across services) — single-process schema federation across plugins is in scope; cross-service subgraph composition is a separate concern.
- The GraphiQL or Altair UI packages.
- Yoga-specific concerns.
Source mapping
Genesis-original. Source of design intent: docs/superpowers/specs/2026-05-13-api-plugin-foundation-design.md.
| Requirement | Design doc section |
|---|---|
| Alternative engine to Yoga | §10.5 |
Same api-graphql contract |
§10.5 |
| Same conformance suite passes | §10.5 ("Both implement the same api-graphql contract; switching is host-config only.") |
| Endpoint set identical to Yoga | §10.6 |
Subscriptions over api-ws |
§10.4 |
| Federation across plugins | §10.2 |
| Engine-agnostic OTel metric names | §12.3 (host emits for free) + §10.5 (swappable) |
Verification approach
src/engine/apollo-engine.test.ts— schema build, request execution, masked errors, depth/complexity guards.src/engine/apollo-engine.spec-conformance.test.ts— runsrunGraphqlEngineConformance("apollo", createApolloGraphqlEngine); MUST pass the exact same invariants asapi-graphql-yoga.src/federation/federation.test.ts— same federation semantics; collision raisesGRAPHQL.SCHEMA_COLLISION.src/subscriptions/subscriptions.test.ts(integration, 30000ms) —graphql-wsoverapi-ws-broker-in-memory.src/persisted-queries/persisted-queries.test.ts— Apollo-specific: persisted-query manifest lookup; unknown id raisesGRAPHQL_APOLLO_PERSISTED_QUERY_NOT_FOUND.tests/integration/engine-swap.test.ts(30000ms) — boots the same plugin set under Yoga then under Apollo; asserts identical query results, identical SDL output, identical metric names (operator dashboards survive the swap).
Migration impact
None — additive only.