gpt4 book ai didi

node.js - GraphQL 中可选但不可为空的字段

转载 作者:搜寻专家 更新时间:2023-10-31 23:36:30 26 4
gpt4 key购买 nike

在我们的 GraphQL API 更新中,只有模型 _id字段是必需的,因此 !在下面的 SDL 语言代码中。其他字段如name不必包含在更新中,但也不能有 null值(value)。目前,不包括 !从名称字段允许最终用户不必传递 name在更新中,但它允许他们传递 null name 的值在,这是不允许的。

A 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
}

当传递一个 null 值时,它会绕过我们的标量,因此如果 null 不是允许的值,我们就不会抛出用户输入验证错误。

我知道 !表示 non-nullable并且缺少 !表示该字段可以为空,但令人沮丧的是,据我所知,如果某个字段不是必需/可选的,我们无法为该字段指定确切的值。此问题仅在更新时出现。

有没有什么方法可以通过自定义标量来解决这个问题,而不必将逻辑硬编码到每个更新解析器中,这看起来很麻烦?

应该失败的示例突变

mutation tests_language_create( $input: language_update! ) { language_update( input: $input ) { name  }}

变量

input: {
_id: "1234",
name: null
}

2018 年 9 月 11 日更新: 作为引用,我找不到解决此问题的方法,因为使用自定义标量、自定义指令和验证规则存在问题。我在 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,
})

编辑:以上仅在您不使用变量时才有效,这在大多数情况下不会很有帮助。作为解决方法,我能够利用 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)
}
}
}

然后在你的架构中:

directive @nonNullInput(paths: [String!]!) on FIELD_DEFINITION

input FooInput {
foo: String
bar: String
}

type Query {
foo (input: FooInput!): String @nonNullInput(paths: ["input.foo"])
}

假设每次在架构中使用 input 时“非空”输入字段都是相同的,您可以将每个 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)
}
}
}

然后在您的架构中:

directive @nonNullInput on FIELD_DEFINITION

type Query {
foo (input: FooInput!): String @nonNullInput
}

关于node.js - GraphQL 中可选但不可为空的字段,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53194116/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com