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

Custom Directives

A custom directive is a schema-transformer function paired with a directive @name declaration. Register the transformer with $directive on a module and Baeta applies it to the compiled schema. For input/argument transforms specifically, see Input Directives; the API there is friendlier than hand-rolling a schema transformer.

Defining a directive

src/modules/custom-native-directive/custom-native-directive.gql

directive @upper on FIELD_DEFINITION

type NameWithUpper {
name: String! @upper
}

type Query {
testUpperDirective(name: String!): NameWithUpper
}

Implementing the directive

Write the directive as a schema-transformer (here using @graphql-tools/utils) and register it with $directive before $schema:

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

import { getDirective, MapperKind, mapSchema } from "@graphql-tools/utils";
import { defaultFieldResolver, type GraphQLSchema } from "graphql";
import { CustomNativeDirectiveModule } from "./typedef.ts";

const { Query, NameWithUpper } = CustomNativeDirectiveModule;

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

function upperDirective(schema: GraphQLSchema) {
return mapSchema(schema, {
[MapperKind.OBJECT_FIELD]: (fieldConfig) => {
const upperDirective = getDirective(schema, fieldConfig, "upper")?.[0];

if (!upperDirective) {
return fieldConfig;
}

const { resolve = defaultFieldResolver } = fieldConfig;

fieldConfig.resolve = async (source, args, context, info) => {
const result = await resolve(source, args, context, info);
if (typeof result === "string") {
return result.toUpperCase();
}
return result;
};
return fieldConfig;
},
});
}

export default CustomNativeDirectiveModule
.$directive(upperDirective)
.$schema({
Query: queryResolver,
NameWithUpper: NameWithUpper.$fields({
name: NameWithUpper.name.key("name"),
}),
});

@upper uppercases string fields. The transformer is chained before $schema so the directive is applied as the module's schema is compiled.

Usage

The directive transforms the result transparently:

query {
testUpperDirective(name: "hello") {
name # Will return "HELLO"
}
}

See the GraphQL spec on directives for the underlying semantics, and the directives example for runnable code.