Files
2026-05-27 23:29:53 +01:00

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 (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.