4.0 KiB
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
GraphqlEnginemethod —buildSchema,executeQuery,executeSubscription,dispose. - Pluggable
instruments,persistedQueries,apolloPluginsdependencies — sane defaults, full override. - Depth + complexity guards run BEFORE Apollo sees the document so the engine rejects pathological queries cheaply.
requestTimeoutMsenforced via a race against asetTimeout- driven rejection — the timer isunref'd so it never holds the event loop open.attachResolversresolves 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: falseflag so the engine raises a typed error before Apollo even validates the document.
Usage: Basic
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:
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<Caps> implements GraphqlEngine<Caps>.constructor(args?: CreateApolloGraphqlEngineArgs)buildSchema(registry): MergedSchemaexecuteQuery(req, ctx): Promise<GraphqlResponse>executeSubscription(req, ctx): AsyncIterable<GraphqlResponse>dispose(): Promise<void>sdl(): string | null
createApolloGraphqlEngine(args?): ApolloGraphqlEngine.raceWithTimeout<T>(p, ms): Promise<T>— 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.