Skip to main content
Version: Next (2.x)

Input Directives

GraphQL doesn't include a built-in way to mutate input values via directives — the spec only covers schema-side transforms. createInputDirective from @baeta/core fills the gap with a small API for reading and writing input values before they reach the resolver.

Target types

Each input directive declares what shape of input it operates on:

  • scalar — primitive values (numbers, strings, booleans).
  • list — array values.
  • object — complex object inputs.

Defining the directive

Declare the directive in the schema:

src/modules/directives/input-directives.gql

directive @increment(by: Int!) on INPUT_FIELD_DEFINITION | ARGUMENT_DEFINITION

type Query {
testIncrementDirective(value: Int! @increment(by: 1)): Int!
}

Implementing the directive

createInputDirective<DirectiveArgs, Context> takes two type parameters:

  • DirectiveArgs — the shape of the directive's arguments in the schema. directive @increment(by: Int!) …{ by: number }.
  • Context — your application's GraphQL context type. Used to type ctx inside resolve.

Register the directive on the module with $directive (same place schema transformers go):

src/modules/custom-input-directive/index.ts

import { createInputDirective } from "@baeta/core";
import type { Context } from "../../types/context.ts";
import { CustomInputDirectiveModule } from "./typedef.ts";

const { Query } = CustomInputDirectiveModule;

const incrementDirective = createInputDirective<{ by: number }, Context>({
name: "increment",
target: "scalar",
resolve: ({ directiveConfig, getValue, setValue }) => {
const value = getValue();
if (typeof value === "number") {
setValue(value + directiveConfig.by);
}
},
});

const queryResolver = Query.$fields({
testIncrementDirective: Query.testIncrementDirective.resolve(({ args }) => {
return args.value;
}),
});

export default CustomInputDirectiveModule
.$directive(incrementDirective)
.$schema({
Query: queryResolver,
});

How it works

The directive runs before the resolver:

  1. getValue() reads the incoming input value.
  2. Your logic computes the new value.
  3. setValue() replaces it. The resolver sees the new value as if it had been sent that way.

Usage

query {
testIncrementDirective(value: 5) {
# value will be incremented to 6 before reaching the resolver
}
}

Use input directives for validation, normalization (trim, lowercase), unit conversion, and any other pre-resolver transform.

See the directives example for runnable code.