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

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 — checks ctx.principal.scopes against the declared scope and throws when missing. Locations: FIELD_DEFINITION.
  • rateLimitDirective — validates the rpm integer 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.