7.3 KiB
Add @suaveplan/api-plugin-graphql
Package: @suaveplan/api-plugin-graphql
Status: PROPOSED
Type: new-package
Why
@suaveplan/api consumers need a typed, builder-driven way to declare GraphQL APIs without authoring raw SDL. The upstream @suaveplan/api-plugin-graphql (suaveplan main, v1.0.4) shipped a self-contained description + resolver-composition layer with SchemaBuilder, TypeBuilder, ResolverComposer, and the defineGraphQLPlugin plugin-definition factory. Genesis must reach functional parity with the upstream surface so existing consumers can swap in without code change while picking up enterprise hardening (typed errors via @suaveplan/error, Zod-validated boundaries, OTel instruments, real EventBus event channel via @suaveplan/events).
What changes
- ADDED:
defineGraphQLPlugin(options)— factory producing aGraphQLPluginDefinition(name + mergedconfig+ lifecyclehooks+getSchema()/getResolvers()accessors). - ADDED:
SchemaBuilder— fluent builder for object / input / enum / interface / union types plusaddQuery/addMutation/addSubscription/build. - ADDED:
TypeBuilder— chainable field declarations consumed bySchemaBuilder. - ADDED:
ResolverComposer—use/useForType/addResolver/addResolvers/hasResolver/build, composing global + per-type middleware around each resolver. - ADDED:
requireRoles(...roles)— auth-check middleware. ThrowsGraphQLError(GRAPHQL.UNAUTHENTICATED)whencontext.useris absent andGraphQLError(GRAPHQL.INSUFFICIENT_ROLE)when no role intersects. - ADDED:
validateArgs(validator)— argument-validation middleware. ThrowsGraphQLError(GRAPHQL.VALIDATION_FAILED)when the validator returns a non-null string. - ADDED:
createQueryExecutedMiddleware(bus?)— wraps a resolver in start/end timing and emits a typedgraphql:query:executedevent onto the providedEventBus(or the package-defaultgraphqlEventsbus). - ADDED:
graphqlEvents— package-defaultEventBus<GraphQLEventMap>forgraphql:query:executedlisteners. - ADDED:
GRAPHQL_CODES+GraphQLError—defineCodes("GRAPHQL", [...])registry with 6 codes:UNAUTHENTICATED,INSUFFICIENT_ROLE,VALIDATION_FAILED,INVALID_INPUT,TYPE_NOT_FOUND,DUPLICATE_RESOLVER. - ADDED: Zod schemas at the public boundary —
DefineGraphQLPluginOptionsSchemavalidates options passed todefineGraphQLPlugin. - ADDED: OTel instruments —
graphql.resolvers_executed_total{outcome}counter andgraphql.resolver_duration_mshistogram emitted fromcreateQueryExecutedMiddleware. - ADDED: Type exports —
SchemaDefinition,TypeDefinition,TypeKind,FieldDefinition,ScalarType,QueryContext,ResolverFn,ResolverInfo,ResolverMiddleware,ResolverMap,GraphQLPluginConfig,GraphQLPluginHooks,GraphQLQueryExecutedEvent,GraphQLEventMap,DefineGraphQLPluginOptions,GraphQLPluginDefinition.
Out of scope
- Live GraphQL execution / HTTP mount. The upstream package is a description + composition layer and does not consume
graphql-jsor mount a Hono route; genesis preserves that contract. A future@suaveplan/api-plugin-graphql-executorpackage (or a follow-up change) will addgraphql-jsexecution + a Hono route — that work is enumerated inenterprise/api-plugin-graphql.md§3 and is explicitly a separate enterprise upgrade, not migration parity. - SDL import/export, federation, persisted queries, subscriptions transport, schema-registry publishing — all enumerated as enterprise upgrades, none present in upstream.
Source mapping
This change derives from ../suaveplan/enterprise/api-plugin-graphql.md and the upstream sources under ../suaveplan/packages/server/api-plugin-graphql/src/.
| Requirement | Source lines / files | Notes |
|---|---|---|
defineGraphQLPlugin factory with default config |
enterprise/api-plugin-graphql.md L13, L22; src/plugin/plugin.ts L83-L116 |
verbatim — basePath=/graphql, enableIntrospection=true, enablePlayground=true, maxDepth=10 |
getSchema() / getResolvers() accessors |
src/plugin/plugin.ts L109-L114 |
verbatim |
| Lifecycle hooks (onInit / onReady / onClose) passthrough | src/plugin/plugin.ts L99-L103 |
verbatim |
SchemaBuilder add{Type,InputType,EnumType,InterfaceType,UnionType,Query,Mutation,Subscription} + hasType/getType/typeCount/queryCount/mutationCount/build |
src/schema-builder/schema-builder.ts L36-L221 |
verbatim |
TypeBuilder.field + fieldCount + build |
src/schema-builder/type-builder.ts L26-L63 |
verbatim |
ResolverComposer use / useForType / addResolver / addResolvers / hasResolver / typeCount / resolverCount / build |
src/resolver/resolver.ts L26-L149 |
verbatim |
| Middleware composition right-to-left | src/resolver/resolver.ts L132-L148 |
verbatim |
requireRoles / validateArgs semantics |
src/resolver/resolver.ts L155-L186 |
upgraded — replaces throw new Error(...) with GraphQLError (enterprise §3 "AuthN/AuthZ", enterprise/api-plugin-graphql.md L48) |
createQueryExecutedMiddleware + graphql:query:executed event |
src/resolver/resolver.ts L208-L234 |
adapted — EventEmitter (upstream) → EventBus<GraphQLEventMap> (genesis events shape) |
graphqlEvents shared bus |
src/resolver/resolver.ts L192 |
adapted — new EventEmitter() → new EventBus<GraphQLEventMap>() |
GraphQLQueryExecutedEvent payload shape |
src/types/graphql.types.ts L160-L171 |
verbatim |
| All public type exports | src/types/graphql.types.ts full + src/index.ts |
verbatim |
defineGraphQLPlugin event shape: GraphQLEventMap["graphql:query:executed"] |
src/types/graphql.types.ts L176-L178 |
verbatim |
Requirements with no source line
| Requirement | Justification |
|---|---|
GRAPHQL_CODES registry + GraphQLError class |
Genesis-wide policy (CLAUDE.md rules + AGENTS.md §13): every package extends SuaveplanError via defineCodes. Upstream threw bare Error(...); flagged in enterprise/api-plugin-graphql.md L37, L48. |
Zod validation at defineGraphQLPlugin boundary |
Genesis-wide policy (AGENTS.md §13 row 2). Upstream did not validate options. |
OTel resolvers_executed_total + resolver_duration_ms |
Genesis-wide policy (AGENTS.md §13 row 5). createQueryExecutedMiddleware already emits the event; instruments are the OTel-side mirror. |
| 100% Istanbul coverage threshold | Genesis-wide policy. |
Verification approach
Every requirement is verified by a concrete it(...) title:
src/plugin/plugin.test.ts—defineGraphQLPlugindefaults, overrides, hooks, accessors, Zod boundary.src/schema-builder/schema-builder.test.ts— every builder method + counts.src/type-builder/type-builder.test.ts—fieldwith all options + defaults.src/resolver/resolver.test.ts—ResolverComposer, middleware composition (global, per-type, combined),requireRoles,validateArgs,createQueryExecutedMiddlewareevent emission.src/error-codes/error-codes.test.ts— everyGRAPHQL_*code thrown.src/api-plugin-graphql.spec-conformance.test.ts— type-level checks (expectTypeOf), every code thrown from a production path, OTel instruments emitted against a realMeterProvider.
Migration impact
None — additive only. Genesis has no prior @suaveplan/api-plugin-graphql package.