Skip to main content

指令

指令可以附加到字段或片段包含,并可以以服务器期望的任何方式影响查询的执行(阅读更多)。 GraphQL 规范提供了几个默认指令:

  • @include(if: Boolean) - 仅在参数为 true 时将此字段包含在结果中
  • @skip(if: Boolean) - 如果参数为 true,则跳过此字段
  • @deprecated(reason: String) - 使用消息标记字段已弃用

指令是一个由@字符引导的标识符,可选择地在命名参数列表之后出现, 它可以出现在 GraphQL 查询和模式语言的几乎任何元素之后。

自定义指令

为了指导当 Apollo/Mercurius 遇到您的指令时应该发生什么,您可以创建一个转换器函数。 此函数使用 mapSchema 函数遍历模式中的位置(字段定义、类型定义等)并执行相应的转换。

import { getDirective, MapperKind, mapSchema } from '@graphql-tools/utils';
import { defaultFieldResolver, GraphQLSchema } from 'graphql';

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

if (upperDirective) {
const { resolve = defaultFieldResolver } = fieldConfig;

// Replace the original resolver with a function that *first* calls
// the original resolver, then converts its result to upper case
fieldConfig.resolve = async function (source, args, context, info) {
const result = await resolve(source, args, context, info);
if (typeof result === 'string') {
return result.toUpperCase();
}
return result;
};
return fieldConfig;
}
},
});
}

现在,在 GraphQLModule#forRoot 方法中使用 transformSchema 函数应用 upperDirectiveTransformer 转换函数:

GraphQLModule.forRoot({
// ...
transformSchema: (schema) => upperDirectiveTransformer(schema, 'upper'),
});

注册后,@upper 指令可以在我们的模式中使用。 然而,应用指令的方式将根据您使用的方法(首选代码方式或模式方式)而有所不同。

代码优先

在首选代码方式中,使用 @Directive() 装饰器应用指令。

@Directive('@upper')
@Field()
title: string;
tip

@Directive() 装饰器是从 @nestjs/graphql 包中导出的。

指令可以应用在字段、字段解析器、输入和对象类型,以及查询、变更和订阅上。 以下是在查询处理程序级别应用指令的示例:

@Directive('@deprecated(reason: "This query will be removed in the next version")')
@Query(returns => Author, { name: 'author' })
async getAuthor(@Args({ name: 'id', type: () => Int }) id: number) {
return this.authorsService.findOneById(id);
}
warning

通过 @Directive() 装饰器应用的指令不会反映在生成的模式定义文件中。

最后,请确保在 GraphQLModule 中声明指令,如下所示:

GraphQLModule.forRoot({
// ...,
transformSchema: schema => upperDirectiveTransformer(schema, 'upper'),
buildSchemaOptions: {
directives: [
new GraphQLDirective({
name: 'upper',
locations: [DirectiveLocation.FIELD_DEFINITION],
}),
],
},
}),
tip

GraphQLDirectiveDirectiveLocation 都是从 graphql 包中导出的。

模式优先

在模式优先方式中,直接在 SDL 中应用指令。

directive @upper on FIELD_DEFINITION

type Post {
id: Int!
title: String! @upper
votes: Int
}