javascripttypescriptgraphqlgraphql-jsgraphql-tools

Unable To Print a Custom Directive in a Generated Schema


I am trying to add a directive at run time which is being generated correctly in the output schema, however I require the directive to be used on certain fields and it's not showing up. Here is how I created the directive

const limitDirective = new graphql.GraphQLDirective({
name: 'myDirective',
locations: [graphql.DirectiveLocation.INPUT_FIELD_DEFINITION, graphql.DirectiveLocation.FIELD_DEFINITION, graphql.DirectiveLocation.ARGUMENT_DEFINITION],
args: {
    constraint: {
        type: graphql.GraphQLString,
        description: 'An example argument for myDirective',
    },
    errorCode: {
        type: graphql.GraphQLString,
        description: 'Another example argument for myDirective',
    },
},
isRepeatable: true,
});

Here is how I integrated it into the InputField using extensions:

 let customInput: graphql.GraphQLInputObjectType = new graphql.GraphQLInputObjectType({
        name: "customInput",
        fields: () => ({
            myField: { type: graphql.GraphQLInt, extensions: {
              myDirective:{constraint:"max=50", errorCode:"LIMIT_EXCEEDED"}
            }}
        })
    })

However when printing the schema with the directives this is not being generated in the resulting schema. I am using the following function to print the schema with directives by graph-tools.

Note: The directive itself is being generated in the resultant file it's not being added to the input field. That's the issue.

Expected Output:

directive @myDirective(
  constraint: String
  errorCode: String
) repeatable on INPUT_FIELD_DEFINITION | FIELD_DEFINITION 

input customInput {
 myField: String @myDirective(constraint: "max=50", errorCode: "LIMIT_EXCEEDED")
}

Actual Output:

directive @myDirective(
  constraint: String
  errorCode: String
) repeatable on INPUT_FIELD_DEFINITION | FIELD_DEFINITION 

input customInput {
 myField: String
}

Solution

  • So the solution is using printSchemaWithDirectives from graphql-tools. This function also takes in an argument for pathToDirectivesInExtensions. Which means in the fields of the input we need to add extensions. The path used in the extension (i.e demoDirectives) is the same path to pass in the printSchemaWithDirectives function. The demoDirectives Object can have any number of directives. And their arguments defined as such

    import { printSchemaWithDirectives, addTypes, makeDirectiveNode, GetDocumentNodeFromSchemaOptions } from '@graphql-tools/utils';
    import * as graphql from 'graphql';
    
    main()
    export async function main() {
      const customDirective = new graphql.GraphQLDirective({ name: 'myDirective', locations: [graphql.DirectiveLocation.INPUT_FIELD_DEFINITION, graphql.DirectiveLocation.FIELD_DEFINITION], args: { constraint: { type: graphql.GraphQLString, description: 'An example argument for myDirective', }, errorCode: { type: graphql.GraphQLString, description: 'Another example argument for myDirective', }, }, isRepeatable: false, });
      let outputSchema =  new graphql.GraphQLSchema({ directives:[customDirective]})
      let customInput: graphql.GraphQLInputObjectType = new graphql.GraphQLInputObjectType({ name: 'customInput', fields: () => ({ myField: { type: graphql.GraphQLInt, extensions: { demoDirectives:{myDirective: {constraint:"abc"}} }}, }) })
      outputSchema = addTypes(outputSchema, [customInput])
      console.log(printSchemaWithDirectives(outputSchema,{pathToDirectivesInExtensions:['demoDirectives']}))
    }
    

    Sample Output:

    directive @myDirective(
      """An example argument for myDirective"""
      constraint: String
      """Another example argument for myDirective"""
      errorCode: String
    ) on INPUT_FIELD_DEFINITION | FIELD_DEFINITION
    
    input customInput {
      myField: Int @myDirective(constraint: "abc")
    }