Catch the highlights of GraphQLConf 2023! Click for recordings. Or check out our recap blog post.
Docs
Writing Plugins
Using Visitor Pattern

Visitor Pattern

Most of the codegen's plugins are written with a design-pattern called Visitor.

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 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 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.

  • BaseTypesVisitor is a class that contains implementation for converting types, interfaces, unions, enums, and fields. It's the base implementation for flow and typescript plugins

  • BaseResolversVisitor is a class that contains implementation for generating a resolvers signature, it's the base implementation for flow-resolvers and typescript-resolvers

  • BaseDocumentsVisitor is class that contains implementation for transforming GraphQL operations (query/mutation/subscription/fragment) with a recursive handler for selection-sets. It's the base implementation for flow-operations and typescript-operations

  • ClientSideBaseVisitor is a class that contains implementation for creating client-side code for consuming GraphQL operations, it's in use by typescript-apollo-angular, typescript-react-apollo, typescript-vue-apollo and typescript-apollo-stencil plugins

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