first commit
This commit is contained in:
100
docs/api-stack/api/api-graphql-yoga/src/engine/yoga-engine.md
Normal file
100
docs/api-stack/api/api-graphql-yoga/src/engine/yoga-engine.md
Normal file
@@ -0,0 +1,100 @@
|
||||
# `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
|
||||
|
||||
```ts
|
||||
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
|
||||
|
||||
```ts
|
||||
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.
|
||||
Reference in New Issue
Block a user