自定义GraphQL Schema指令@nonEmptyArray无法触发验证问题
问题分析与解决方案
问题出在GraphQL对列表类型的包装处理机制上:你给输入对象字段添加了@nonEmptyArray指令,但字段类型是[String!]!(即NonNull(List(NonNull(String)))),而你把验证逻辑添加到了INPUT_OBJECT_FIELD的配置扩展中,并没有关联到实际处理数组输入的List类型上,导致validateInput始终不会被触发。
修正方案
核心思路是:解析输入字段的类型结构,找到其中的List类型部分,将验证逻辑绑定到该List类型的扩展上,这样数组输入经过List类型处理时,就会执行空数组检查。
修正后的完整代码
import { GraphQLSchema, GraphQLInputFieldConfig, isListType, GraphQLNonNull } from 'graphql'; import { MapperKind, mapSchema, getDirective, SchemaMapper } from '@graphql-tools/utils'; class EmptyArrayError extends Error { constructor(fieldName: string) { super(`Field "${fieldName}" cannot be an empty array`); this.name = 'EmptyArrayError'; } } const getNonEmptyArrayDirective = (schema: GraphQLSchema): SchemaMapper => ({ [MapperKind.INPUT_OBJECT_FIELD]: ( fieldConfig: GraphQLInputFieldConfig, fieldName: string, typeName: string, ) => { const directive = getDirective(schema, fieldConfig, 'nonEmptyArray')?.[0]; if (directive) { // 逐层解析字段类型,穿透NonNull包装,找到实际的List类型 let currentType = fieldConfig.type; while (currentType instanceof GraphQLNonNull) { currentType = currentType.ofType; } // 确认是List类型后,添加验证逻辑到其扩展中 if (isListType(currentType)) { currentType.extensions = { ...currentType.extensions, validateInput: (inputValue: unknown) => { if (Array.isArray(inputValue) && inputValue.length === 0) { throw new EmptyArrayError(fieldName); } return inputValue; }, }; } } return fieldConfig; }, }); export default function applySchemaDirectives( schema: GraphQLSchema, ): GraphQLSchema { return mapSchema(schema, getNonEmptyArrayDirective(schema)); }
关键说明
- 类型解析:通过循环穿透
NonNull包装,定位到实际处理数组的List类型,确保验证逻辑绑定到正确的处理节点上。 - 验证触发时机:
List类型的validateInput会在数组输入被解析时执行,此时就能直接拿到完整的数组值进行空检查。 - 错误处理:自定义的
EmptyArrayError会被GraphQL的错误处理机制捕获,最终返回给客户端清晰的错误提示。
内容的提问来源于stack exchange,提问作者Dan Mandel
相关产品推荐
相关产品推荐

