3.0 KiB
directives
(no submodules)
Purpose
Host-supplied GraphQL directive descriptors that engine adapters install at
schema-build time. The contract layer ships two: @auth(scope: String!) for
per-field authorisation and @rateLimit(rpm: Int!) for per-field rate
limiting. Plugins reference them in SDL; engines wire the descriptor's
handler into the resolver pipeline.
Keeping the descriptors here (rather than in each engine adapter) means a
plugin author writes one piece of SDL and gets the same enforcement
semantics from every concrete engine. Engine adapters install the SDL via
the directive sdl string and dispatch to the descriptor's handler from
their resolver wrapper.
Features
authDirective— checksctx.principal.scopesagainst the declared scope and throws when missing. Locations:FIELD_DEFINITION.rateLimitDirective— validates therpminteger bound; the actual rate-limit accounting is the engine adapter's responsibility (the contract body asserts the argument shape).HOST_DIRECTIVES— frozen map keyed by directive name for engine adapter consumption.
Usage: Basic
import { authDirective } from "@suaveplan/api-graphql/directives";
// SDL declaration:
// directive @auth(scope: String!) on FIELD_DEFINITION
//
// Used in a plugin's typeDefs:
// type Query { invoices: [Invoice!]! @auth(scope: "billing:read") }
console.log(authDirective.sdl);
Usage: Advanced
Engine adapters install every host directive in one pass:
import { HOST_DIRECTIVES } from "@suaveplan/api-graphql/directives";
for (const directive of Object.values(HOST_DIRECTIVES)) {
schemaBuilder.addDirective({
name: directive.name,
sdl: directive.sdl,
run: directive.handler,
});
}
The handler is the contract-layer enforcement body. The @auth handler
reads ctx.principal.scopes (the typed readonly Scope[]) and raises a
plain Error when the required scope is absent — engine adapters wrap that
in the engine's GraphQL error type before returning it on the wire.
API Reference
authDirective: DirectiveDescriptor<{ scope: string }>— descriptor for@auth.rateLimitDirective: DirectiveDescriptor<{ rpm: number }>— descriptor for@rateLimit.HOST_DIRECTIVES— frozen{ auth, rateLimit }lookup.
Implementation Notes
The descriptors carry contract bodies, not full implementations. A
production engine adapter wraps each resolver in a check that runs the
handler before invoking the resolver. For @rateLimit, the engine adapter
also wires the per-tenant counter; the contract-layer handler only enforces
the rpm > 0 invariant so a misconfigured directive surfaces early.
Adding a new host directive: add a DirectiveDescriptor, append it to
HOST_DIRECTIVES, and document the SDL in the package README. The
api-graphql-yoga and api-graphql-apollo packages pick up new
descriptors automatically through the frozen map.