Table of Contents
Hooks
The most common thing for a plugin to do is to add hooks to the builder.
The GraphQL hooks ran by
newWithHooks
allow you to manipulate the argument (specification) that is being passed to the
GraphQL object constructors before the objects are constructed. You can think of
hooks as wrappers around the original object spec, like this:
const MyType = newWithHooks(GraphQLObjectType, spec);
// is equivalent to:
const MyType = new GraphQLObjectType(hook3(hook2(hook1(spec))));
Hooks are registered via a call to
builder.hook(hookName, hookFunction)
.
Every hookFunction
must synchronously return a value - either the value that
it was passed in as the first argument or a derivative of it (preferably
immutable, but we're not strict on that).
Hook functions for one hook by default run in the order they were registered, that is why the order of plugins is sometimes relevant.
Stages of the build process
The hookName
that you register the function for must match
one of the supported hooks.
Here's a brief rundown of how the hooks are used by the Graphile engine. See the
buildSchema
and
createBuild
methods in the source code.
- A new Build object with the basic
functionality is created by
makeNewBuild
- The
inflection
hook allows plugins to add new inflection methods to thebuild.inflection
object, or overwrite previously declared ones. - The
build
hook allows plugins to add new utility methods to thebuild
object itself, or overwrite previously declared ones. - The
init
hook is the setup phase where the schema types get created by the plugins, using thenewWithHooks
(preferred) oraddType
(discouraged) methods of the build object. For example, the defaultStandardTypesPlugin
registers the builtin scalars, or the PostGraphile plugins register the types found in the introspection results. - The schema is constructed using
newWithHooks(GraphQLSchema, …)
, where thequery
,mutation
andsubscription
root operations are configured by the respective default plugins and other schema options can be adjusted. - The
finalize
hook allows plugins to replace the schema that has been built with an alternative (likely derivative) schema, should that be desired. It also opens an opportunity to do something with the built schema (for example log it out) before it is returned.
During the init
hook, the
hooks for the respective GraphQL types are applied
in the newWithHooks
function,
which may cause them to run in a nested fashion.
This hook system makes the library both powerful and flexible, at the expense of
traceability - instead of having a clear declarative import
, the origin of a
called method might be in any of the used plugins, or even multiple ones. See
PostGraphile's Debugging instructions
for how to alleviate this.
What to do when that hook fires: hookFunction
The hookFunction
that you register via builder.hook(hookName, hookFunction)
will be called with 3 arguments:
- The input object (e.g. the spec that would be passed to the GraphQLObjectType constructor)
- The
Build
object (see below) - The
Context
object (see below) which contains ascope
property
Build object (Build
)
The Build Object contains a number of helpers and sources of information relevant to the current build of the GraphQL API. If you're in watch mode then every time a new schema is generated a new build object will be used.
Plugins may extend the build
object via the build
hook. Once the build
hook is complete the build object is frozen.
The most commonly used methods are:
build.extend(obj1, obj2)
- returns a new object based on a non-destructive merge ofobj1
andobj2
(will not overwrite keys!) - normally used at the return value for a hookbuild.graphql
- equivalent torequire('graphql')
, but helps ensure GraphQL version clashes do not occurbuild.inflection
- carries all the inflector functions for names
See Build Object for the rest.
Context object (Context
)
The Context Object contains the information
relevant to the current hook. Most importantly it contains the scope
(an
object based on the third argument passed to newWithHooks
) but it also
contains a number of other useful things. Here's some of the more commonly used
ones:
scope
- an object based on the third argument tonewWithHooks
orfieldWithHooks
; for deeper hooks (such asGraphQLObjectType:fields:field
) the scope from shallower hooks (such asGraphQLObjectType
) are merged in.Self
- only available on deferred hooks (those that are called after the object is created, e.g.GraphQLObjectType:fields
) this is the object that has been created, allowing recursive references.fieldWithHooks(fieldName, spec, scope = {})
- onGraphQLObjectType:fields
, used for adding a field if you need access to the field helpers (or want to define a scope)
Namespaces
Properties added to the Build
object or set on the Context.scope
should be
namespaced so that they do not conflict; for example postgraphile
uses the
pg
namespace: pgSql
, pgIntrospection
, isPgTableType
, etc