Docs
Writing Plugins
Using Visitor Pattern

Visitor Pattern

Most of the codegen's plugins are written with a design-pattern called Visitor (opens in a new tab).

In addition, GraphQL has an internal mechanism for "visiting" GraphQLSchema and GraphQL operations, and you can use it to transform your GraphQL definitions into a custom output.

You can call a custom function on each AST node and transform it into something else with a visitor pattern.

You can use ASTExplorer (opens in a new tab) and see how GraphQL represents its definitions in a JSON structure. You can also use this to understand which function will be called each time.

In graphql.org (opens in a new tab) you can find the detailed API documentation we will use in this section.

Basic Visitor

In this example, we will transform a basic type definition into a list of types and fields:

From:

type MyType {
  myField: String!
}
 
type MyOtherType {
  myOtherField: Int!
}

To

MyType.myField
MyOtherType.myOtherField

To get started with a basic visitor, start by extracting the astNode of your GraphQLSchema:

const { getCachedDocumentNodeFromSchema } = require('@graphql-codegen/plugin-helpers')
 
module.exports = {
  plugin(schema, documents, config) {
    const astNode = getCachedDocumentNodeFromSchema(schema) // Transforms the GraphQLSchema into ASTNode
  }
}

Then, create your initial visitor, in our case, we would like to transform a FieldDefinition and ObjectTypeDefinition, so let's create an object with a stub definitions, an use visit to run it:

const { getCachedDocumentNodeFromSchema } = require('@graphql-codegen/plugin-helpers')
const { visit } = require('graphql')
 
module.exports = {
  plugin(schema, documents, config) {
    const astNode = getCachedDocumentNodeFromSchema(schema) // Transforms the GraphQLSchema into ASTNode
    const visitor = {
      FieldDefinition(node) {
        // This function triggered per each field
      },
      ObjectTypeDefinition(node) {
        // This function triggered per each type
      }
    }
 
    const result = visit(astNode, { leave: visitor })
 
    return result.definitions.join('\n')
  }
}

Now, let's implement ObjectTypeDefinition and FieldDefinition:

const { getCachedDocumentNodeFromSchema } = require('@graphql-codegen/plugin-helpers')
const { visit } = require('graphql')
 
module.exports = {
  plugin(schema, documents, config) {
    const astNode = getCachedDocumentNodeFromSchema(schema) // Transforms the GraphQLSchema into ASTNode
    const visitor = {
      FieldDefinition(node) {
        // Transform the field AST node into a string, containing only the name of the field
        return node.name.value
      },
      ObjectTypeDefinition(node) {
        // "node.fields" is an array of strings, because we transformed it using "FieldDefinition".
        return node.fields.map(field => `${node.name.value}.${field}`).join('\n')
      }
    }
 
    const result = visit(astNode, { leave: visitor })
 
    return result.definitions.join('\n')
  }
}

Codegen and Visitors

This repository also contains a set of utils that might help you to write plugins faster using the visitor pattern.

All those utils are part of @graphql-codegen/visitor-plugin-common package.

It includes a set of Visitor classes that you can use and extend to implement your plugin quickly:

For example, BaseVisitor is a class that contains a simple implementation and utils for plugin configuration and lets you quickly implement plugins compatible with namingConvention and scalars configuration.

You can find an example for using it here (opens in a new tab).

To create a custom plugin, you can use the above classes as a base and extend it as you wish.