# `engine/apollo-engine` — `ApolloGraphqlEngine` _(no submodules)_ ## Purpose `ApolloGraphqlEngine` is the concrete Apollo Server implementation of the `@suaveplan/api-graphql` engine contract. It owns the Apollo Server instance, the merged executable schema, the persisted-query manifest plumbing, the request-time depth and complexity guards, and the OTel instrument emission. The factory `createApolloGraphqlEngine` hands out fresh instances; the class is `new`-able for tests and power users that want subclass overrides. Every operation pivots on three lifecycle calls: `buildSchema` once at boot (or on hot reload), `executeQuery` per request, `dispose` at shutdown. Schema rebuild gracefully tears down the prior Apollo instance only when its startup has completed — a half-started server is left alone, which is safe because the new server replaces the slot. ## Features - Implements every `GraphqlEngine` method — `buildSchema`, `executeQuery`, `executeSubscription`, `dispose`. - Pluggable `instruments`, `persistedQueries`, `apolloPlugins` dependencies — sane defaults, full override. - Depth + complexity guards run BEFORE Apollo sees the document so the engine rejects pathological queries cheaply. - `requestTimeoutMs` enforced via a race against a `setTimeout`- driven rejection — the timer is `unref`'d so it never holds the event loop open. - `attachResolvers` resolves SDL-declared fields against the registry's resolver maps; unknown types and unknown fields are silently skipped so plugin authors can register superset maps without runtime breakage. - Introspection denial layered above Apollo's `introspection: false` flag so the engine raises a typed error before Apollo even validates the document. ## Usage: Basic ```ts import { createGraphqlRegistry } from "@suaveplan/api-graphql"; import { createApolloGraphqlEngine } from "@suaveplan/api-graphql-apollo"; const engine = createApolloGraphqlEngine(); const registry = createGraphqlRegistry(); registry.addTypes("type Query { ping: String! }"); registry.addResolvers({ Query: { ping: () => "pong" } }); engine.buildSchema(registry); const result = await engine.executeQuery( { query: "{ ping }" }, requestContext ); await engine.dispose(); ``` ## Usage: Advanced Wire persisted queries, custom instruments, a 5-second timeout, and a custom Apollo plugin for cache control: ```ts import { createApolloGraphqlEngine } from "@suaveplan/api-graphql-apollo"; import { createInstruments } from "@suaveplan/api-graphql-apollo/instruments"; import { createPersistedQueryManifest } from "@suaveplan/api-graphql-apollo/persisted-queries"; const instruments = createInstruments(); const persistedQueries = createPersistedQueryManifest([ ["abc123", "{ ping }"], ]); const engine = createApolloGraphqlEngine({ instruments, persistedQueries, apolloPlugins: [cacheControlPlugin], options: { requestTimeoutMs: 5000, maxQueryDepth: 12 }, }); ``` ## API Reference - `class ApolloGraphqlEngine implements GraphqlEngine`. - `constructor(args?: CreateApolloGraphqlEngineArgs)` - `buildSchema(registry): MergedSchema` - `executeQuery(req, ctx): Promise` - `executeSubscription(req, ctx): AsyncIterable` - `dispose(): Promise` - `sdl(): string | null` - `createApolloGraphqlEngine(args?): ApolloGraphqlEngine`. - `raceWithTimeout(p, ms): Promise` — exported for unit testing. ## Implementation Notes `startInBackgroundHandlingStartupErrorsByLoggingAndFailingAllRequests` was rejected in favour of an explicit `start()` Promise because the former turns a startup failure into per-request rejections we cannot discriminate cleanly. The current implementation awaits the start promise inside `executeQuery` and again inside `dispose` so a failed start never leaves the engine in a half-stopped state. The `raceWithTimeout` helper is exported so a focused unit test can exercise both the resolve and reject branches without standing up an Apollo Server.