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).
-
BaseTypesVisitor
is 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 -
BaseResolversVisitor
is 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) -
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 forflow-operations
(opens in a new tab) andtypescript-operations
(opens in a new tab) -
ClientSideBaseVisitor
is 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-apollo
andtypescript-apollo-stencil
plugins
To create a custom plugin, you can use the above classes as a base and extend it as you wish.