Files
skills/docs/api-stack/api/api-graphql-yoga/src/engine/yoga-engine.md
2026-05-29 08:20:56 +01:00

3.8 KiB

yoga-engine.ts

Purpose

Implements the abstract GraphqlEngine contract from @suaveplan/api-graphql over the Yoga / graphql-js stack. The factory createYogaGraphqlEngine(config) returns an engine that hosts can wire into api-host's adapter slot — it is the default GraphQL engine per design doc §10.5.

The engine handles the full pipeline for every inbound request: persisted- query resolution, document parse, introspection guard, depth+complexity guards, validate, execute (with wall-clock timeout), and OTel publish. buildSchema federates plugin contributions via @graphql-tools/schema's makeExecutableSchema, increments graphql.schema_rebuilds_total, and caches the executable schema. executeSubscription parses + validates and returns the GraphQL subscription's AsyncIterable, tracking the live count via activeSubscriptions() so the observable gauge can sample it.

Features

  • Schema federation across multiple plugin registries, with GRAPHQL.SCHEMA_COLLISION raised at the abstract layer.
  • 10 GRAPHQL_YOGA.* codes wired into the production paths.
  • Masked-errors and introspection-toggle behaviour controllable per host.
  • Per-request OTel counter + histogram emission with masked / unmasked outcome labels.
  • Persisted-query lookup via injectable Map<string, string> so the host can hot-update the registry without rebuilding the engine.
  • Observable subscriptions_active gauge that reflects the live count of executeSubscription iterators.

Usage: Basic

import { createGraphqlRegistry } from "@suaveplan/api-graphql";
import { createYogaGraphqlEngine } from "@suaveplan/api-graphql-yoga";

const engine = createYogaGraphqlEngine();
const registry = createGraphqlRegistry();
registry.addTypes("type Query { ping: String! }");
registry.addResolvers({ Query: { ping: () => "pong" } });
engine.buildSchema(registry);

const result = await engine.executeQuery(
    { query: "{ ping }" },
    {} as never
);
// result.data.ping === "pong"
await engine.dispose();

Usage: Advanced

import {
    createYogaGraphqlEngine,
    federatePlugins,
} from "@suaveplan/api-graphql-yoga";
import { createGraphqlRegistry } from "@suaveplan/api-graphql";
import { createInMemoryWsBroker } from "@suaveplan/api-ws-broker-in-memory";

const broker = createInMemoryWsBroker();
const billing = createGraphqlRegistry();
billing.addTypes("extend type Query { invoices: [String!]! }");
billing.addResolvers({ Query: { invoices: () => ["i_1"] } });

const engine = createYogaGraphqlEngine({
    options: { maxQueryDepth: 8, maxQueryComplexity: 250 },
    entries: [{ plugin: "billing", registry: billing as never }],
    wsBroker: broker,
    persistedQueries: new Map([["pq_1", "{ invoices }"]]),
});
const inline = createGraphqlRegistry();
inline.addTypes("type Query { ping: String! }");
inline.addResolvers({ Query: { ping: () => "pong" } });
engine.buildSchema(inline);

API Reference

  • YogaEngineConfig — { options?, entries?, wsBroker?, persistedQueries? }.
  • YogaGraphqlEngine<Caps> — engine handle extending GraphqlEngine<Caps>.
  • createYogaGraphqlEngine<Caps>(config?) — factory.

Implementation Notes

buildSchema accepts a runtime-supplied registry alongside the pre- registered config.entries; the runtime registry is appended as a final __inline__ plugin so callers that don't manage entries up-front (e.g. the conformance harness) still drive the engine through the same path.

The introspection guard runs against the raw query text rather than the parsed AST so that pre-parse, pre-guard rejection paths still produce a deterministic GRAPHQL_YOGA.INTROSPECTION_DENIED instead of leaking parse errors. The depth/complexity guard ALWAYS runs before validate() so denial-of-service queries cost a parse + a walk, not a parse + validate + execute.