# `@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` (default `false`), `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). - ADDED: Subscriptions — same `graphql-ws` over `api-ws` bridge 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: `./testing` subpath — re-export of `runGraphqlEngineConformance` bound to `createApolloGraphqlEngine`. ## Out of scope - The abstract `api-graphql` contract. - 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` — runs `runGraphqlEngineConformance("apollo", createApolloGraphqlEngine)`; MUST pass the exact same invariants as `api-graphql-yoga`. - `src/federation/federation.test.ts` — same federation semantics; collision raises `GRAPHQL.SCHEMA_COLLISION`. - `src/subscriptions/subscriptions.test.ts` (integration, 30000ms) — `graphql-ws` over `api-ws-broker-in-memory`. - `src/persisted-queries/persisted-queries.test.ts` — Apollo-specific: persisted-query manifest lookup; unknown id raises `GRAPHQL_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.