3

I'm building a custom directive in which I'm hoping to validate entire input objects. I'm using the INPUT_OBJECT type with the visitInputObject method on SchemaDirectiveVisitor extended class.

Every time I run a mutation using the input type then visitInputObject does not run. I've used the other types/methods like visitObject and visitFieldDefinition and they work perfectly. But when trying to use input types and methods they will not trigger.

I've read all the available documentation I can find. Is this just not supported yet?

Some context code(Not actual):

directive @validateThis on INPUT_OBJECT

input MyInputType @validateThis {
  id: ID
  someField: String
}
type Mutation {
  someMutation(myInput: MyInputType!): SomeType
}
class ValidateThisDirective extends SchemaDirectiveVisitor {
  visitInputObject(type) {
    console.log('Not triggering');
  }
}

1 Answers1

0

All the visit methods of a SchemaDirectiveVisitor are ran at the same time -- when the schema is built. That includes visitFieldDefinition and visitFieldDefinition. The difference is that when we use visitFieldDefinition, we often do it to modify the resolve function for the visited field. It's this function that's called during execution.

You use each visit methods to modify the respective schema element. You can use visitInputObject to modify an input object, for example to add or remove fields from it. You cannot use it to modify the resolution logic of an output object's field. You should use visitFieldDefinition for that.

visitFieldDefinition(field, details) {
const { resolve = defaultFieldResolver } = field
  field.resolve = async function (parent, args, context, info) {
    Object.keys(args).forEach(argName => {
      const argDefinition = field.args.find(a => a.name === argName)
      // Note: you may have to "unwrap" the type if it's a list or non-null
      const argType = argDefinition.type
      if (argType.name === 'InputTypeToValidate') {
        const argValue = args[argName]
        // validate here
      } 
    })    

    return resolve.apply(this, [parent, args, context, info]);
  }
}
Daniel Rearden
  • 80,636
  • 11
  • 185
  • 183
  • Thanks for the response. So input type fields don't have a resolve method it seems? So how might I do validation on an input object as a whole? With `visitObject` it allows me to edit the resolve method of the fields and then get the whole type object field values from the first argument of the resolve method I passed. – FunToCodeJavascript Mar 04 '20 at 00:56
  • Yes, only fields are resolved -- the resolvers are what's executed to generate the values returned in the response. A resolver for inputs wouldn't make sense -- their values are already provided. Like I said, you'd need to use `visitFieldDefinition` (or `visitObject`) and modify the resolver logic to include the validation logic. If you only want to do the validation for specific types of arguments, you can inspect the field object to determine the types of its arguments. – Daniel Rearden Mar 04 '20 at 01:04
  • Right, makes sense. The part where I'm stuck is I'm not trying to validate it on its way out, but instead on its way in from the input. `visitObject` is for outgoing data as far as I know. I was thinking there might be a convenient way to validate on the way in similarly to how it's done on the way out, but sounds like the only validation you can do going in is type checking and at most type checking against custom scalars for individual fields. Thanks for the clarification! – FunToCodeJavascript Mar 04 '20 at 01:09
  • @FunToCodeJavascript Maybe I'm not explaining it sufficiently. You can do the validation *before* the resolver code is ran. I edited the question with a rough example. As an aside, you could use custom scalars, but it's generally better to have the validation logic inside your business layer anyway, and not part of the API layer -- it's more reusable that way. – Daniel Rearden Mar 04 '20 at 01:25
  • Oh I misunderstood. Thought by the time that code could be run then the data would already be in my database, but if I validate and halt it there in the `field.resolve` it actually stops the mutation resolver from kicking off. I had written it off, thanks for the persistent help and I apologize for the uptake. I'm excited to use this. – FunToCodeJavascript Mar 04 '20 at 01:48