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.myOtherFieldTo 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).
-
BaseTypesVisitoris a class that contains implementation for converting types, interfaces, unions, enums, and fields. It's the base implementation forflow(opens in a new tab) andtypescript(opens in a new tab) plugins -
BaseResolversVisitoris a class that contains implementation for generating a resolvers signature, it's the base implementation forflow-resolvers(opens in a new tab) andtypescript-resolvers(opens in a new tab) -
BaseDocumentsVisitoris class that contains implementation for transforming GraphQL operations (query/mutation/subscription/fragment) with a recursive handler for selection-sets. It's the base implementation forflow-operations(opens in a new tab) andtypescript-operations(opens in a new tab) -
ClientSideBaseVisitoris a class that contains implementation for creating client-side code for consuming GraphQL operations, it's in use bytypescript-apollo-angular,typescript-react-apollo,typescript-vue-apolloandtypescript-apollo-stencilplugins
To create a custom plugin, you can use the above classes as a base and extend it as you wish.