Mat*_*w P 3 node.js graphql graphql-js apollo-server
在更新我们的GraphQL API时,仅_id需要model 字段,因此!在下面的SDL语言代码中。其他字段(例如,name不必包含在更新中)也不能具有null值。当前,!从名称字段中排除namein 允许最终用户不必在更新中传递in,但允许最终用户传递in的null值name,这是不允许的。
一个null值使我们知道需要从数据库中删除一个字段。
下面是一个可能导致问题的模型示例- Name自定义标量不允许空值,但GraphQL仍允许它们通过:
type language {
_id: ObjectId
iso: Language_ISO
auto_translate: Boolean
name: Name
updated_at: Date_time
created_at: Date_time
}
input language_create {
iso: Language_ISO!
auto_translate: Boolean
name: Name!
}
input language_update {
_id: ObjectId!
iso: Language_ISO!
auto_translate: Boolean
name: Name
}
Run Code Online (Sandbox Code Playgroud)
当传入null值时,它会绕过我们的Scalars,因此如果null不是允许的值,我们就不会引发用户输入验证错误。
我知道这!意味着non-nullable,并且缺少!手段意味着该字段可为空,但是令人沮丧的是,据我所知,如果某个字段不是必需的/可选的,我们无法为该字段指定确切的值。仅在更新时会发生此问题。
有什么方法可以通过自定义Scalars解决此问题,而不必在每个更新解析器中启动硬编码逻辑,而这似乎很麻烦?
可能失败的示例变异
mutation tests_language_create( $input: language_update! ) { language_update( input: $input ) { name }}
Run Code Online (Sandbox Code Playgroud)
变数
input: {
_id: "1234",
name: null
}
Run Code Online (Sandbox Code Playgroud)
更新9/11/18:供参考,我找不到解决方法,因为使用自定义标量,自定义指令和验证规则存在问题。我在GitHub上打开了一个问题:https://github.com/apollographql/apollo-server/issues/1942
您真正要寻找的是自定义验证逻辑。您可以在构建架构时通常包含的“默认”集之上添加所需的任何验证规则。这是一个粗略的示例,说明如何添加规则以在将特定类型或标量用作参数时检查空值:
const { specifiedRules } = require('graphql/validation')
const { GraphQLError } = require('graphql/error')
const typesToValidate = ['Foo', 'Bar']
// This returns a "Visitor" whose properties get called for
// each node in the document that matches the property's name
function CustomInputFieldsNonNull(context) {
return {
Argument(node) {
const argDef = context.getArgument();
const checkType = typesToValidate.includes(argDef.astNode.type.name.value)
if (checkType && node.value.kind === 'NullValue') {
context.reportError(
new GraphQLError(
`Type ${argDef.astNode.type.name.value} cannot be null`,
node,
),
)
}
},
}
}
// We're going to override the validation rules, so we want to grab
// the existing set of rules and just add on to it
const validationRules = specifiedRules.concat(CustomInputFieldsNonNull)
const server = new ApolloServer({
typeDefs,
resolvers,
validationRules,
})
Run Code Online (Sandbox Code Playgroud)
编辑:以上内容仅在您不使用变量的情况下有效,这在大多数情况下不会很有帮助。解决方法是,我能够利用FIELD_DEFINITION指令来实现所需的行为。您可能有多种方法可以解决此问题,但这是一个基本示例:
class NonNullInputDirective extends SchemaDirectiveVisitor {
visitFieldDefinition(field) {
const { resolve = defaultFieldResolver } = field
const { args: { paths } } = this
field.resolve = async function (...resolverArgs) {
const fieldArgs = resolverArgs[1]
for (const path of paths) {
if (_.get(fieldArgs, path) === null) {
throw new Error(`${path} cannot be null`)
}
}
return resolve.apply(this, resolverArgs)
}
}
}
Run Code Online (Sandbox Code Playgroud)
然后在您的架构中:
directive @nonNullInput(paths: [String!]!) on FIELD_DEFINITION
input FooInput {
foo: String
bar: String
}
type Query {
foo (input: FooInput!): String @nonNullInput(paths: ["input.foo"])
}
Run Code Online (Sandbox Code Playgroud)
假设每次input在架构中使用“ null”输入字段都相同时,可以将每个input名称映射到应验证的字段名称数组。因此,您也可以执行以下操作:
const nonNullFieldMap = {
FooInput: ['foo'],
}
class NonNullInputDirective extends SchemaDirectiveVisitor {
visitFieldDefinition(field) {
const { resolve = defaultFieldResolver } = field
const visitedTypeArgs = this.visitedType.args
field.resolve = async function (...resolverArgs) {
const fieldArgs = resolverArgs[1]
visitedTypeArgs.forEach(arg => {
const argType = arg.type.toString().replace("!", "")
const nonNullFields = nonNullFieldMap[argType]
nonNullFields.forEach(nonNullField => {
const path = `${arg.name}.${nonNullField}`
if (_.get(fieldArgs, path) === null) {
throw new Error(`${path} cannot be null`)
}
})
})
return resolve.apply(this, resolverArgs)
}
}
}
Run Code Online (Sandbox Code Playgroud)
然后在您的架构中:
directive @nonNullInput on FIELD_DEFINITION
type Query {
foo (input: FooInput!): String @nonNullInput
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
949 次 |
| 最近记录: |