GraphQL Executor
A customizable GraphQL Spec compliant Executor based on graphql-js
Installation
With npm:
npm install --save graphql-executor
or using yarn:
yarn add graphql-executor
Usage
For the default GraphQL-Executor behavior:
- Use the
execute
and subscribe
methods exported by graphql-executor
in place of those exported by graphql-js
.
To customize execution:
(A) fork this package, customize the code, and carry on as above, or
(B) override the internal Executor
class:
- Remember to pin the version of
graphql-executor
you use, as the Executor
class remains internal with all changes considered non-breaking. - Subclass the exported
Executor
class. - Override your subclassed
Executor
methods as desired to provide your custom functionality. - Create a new custom
execute
function calling this subclass.
import { Executor } from 'graphql-executor';
class MyCustomExecutor extends Executor {
}
export function execute(args: ExecutionArgs): PromiseOrValue<ExecutionResult> {
const executor = new MyCustomExecutor(args);
return executor.executeQueryOrMutation(args);
}
Why GraphQL Executor?
GraphQL Executor provides:
- A way to fork the GraphQL.JS executor without introducing multiple versions of
graphql-js
into your project. graphql-executor
is a "smart" fork of only the
execution
module of graphql-js
. You can safely fork graphql-executor
to
customize your execution flow as needed. - A code-only method of customizing the executor by subclassing the exported internal
Executor
class as above. - Direct benefits from our own customizations! GraphQL Executor is spec-compliant,
but also supports experimental features (such as
@defer
/@stream
support) and provide
other improvements as possible. See (https://github.com/yaacovCR/graphql-executor/releases)
to track any new features.
The Long Version
GraphQL.js occupies a foundational position
within the Javascript GraphQL ecosystem as the central dependency for almost all other
community packages. GraphQL.js provides two key capabilities: building a typed schema
and executing requests against that type schema; it also includes a parser for the
GraphQL language and a handful of additional extremely helpful utilities. The vast
majority of community packages depend in some way upon this bedrock to provide all
additional custom functionality.
GraphQL.js allows for customization in several ways. SDL directives allow schema
and operation writers to specify custom behavior while still using the standard GraphQL
parser. extensions
fields on most objects allow for setting and passing custom
metadata. In many ways, execution can be customized within resolver functions, perhaps
in combination with the above custom directives and extensions.
Customization of the execution algorithm itself is less straightforward, if not
impossible. GraphQL.js serves as a reference implementation for the
GraphQL specification. This means that the execution
algorithm included within GraphQL.js is designed not only to be spec-compliant, but
also to most clearly express the algorithms contained within the spec. While
experimentation is encouraged, this is in the context of pull requests to the
specification, which should have a matching pull request within the reference
implementation. Execution algorithm customization is not the main concern of
GraphQL.js, and is usually therefore handled to the extent possible outside the
execution pipeline within the calling code.
For example, take note of Envelop, a powerful,
server-agnostic method of customizing the GraphQL request layer. Note that the
available execution hooks
are limited to onExecuteDone
and onResolverCalled
, among a few others.
GraphQL.JS provides a hook for field resolution, but not for field completion,
for example, limiting the hooks that Envelop can provide. Free-form user customization
of execution cuts against the role of GraphQL.JS as a strict, spec-conformant reference
implementation. Introduction of middleware in GraphQL.JS has
proved
elusive.
GraphQL Executor fills this gap by allowing for customization of the entire execution
pipeline. Perhaps the most straightforward method of customizing execution would be to
simply fork the graphql-js
package for one's own custom use. This is made difficult by
the central position of GraphQL.js in the ecosystem; most projects can safely tolerate
only a single version of graphql-js
, see
graphql-js#594 and
"GraphQL.js: Preparing for v14.0.0".
GraphQL Executor is a "smartly" forked version of the graphql.js
package that includes
only the execution
module and still depends on the graphql-js
package for all other
purposes, thereby eliding the issues above.
GraphQL Executor also refactors the execution pipeline from GraphQL.js into an exported
versioned, an Executor
class. An execute
function is simply a thin wrapper around
the Executor.executeQueryOrMutation(args)
and Executor.executeSubscription(args)
methods. You can subclass the Executor
class to customize the executor pipeline without
maintaining a separate repository. The Executor
class is internal, so when utilizing this
method, you must pin the version of the graphql-executor
package.
Contributing
We actively welcome pull requests. GraphQL Executor is spec-compliant but is open to adopting
experimental behavior (such as the already supported @defer
/@stream
directives).
Changelog
We use changesets
for release management.
Changes are therefore tracked as GitHub releases.
License
GraphQL Executor is MIT-licensed.
Credits
Credit for GraphQL Executor is due mostly to all the hard work done at
graphql-js, and especially @IvanGoncharov, who
provided close direction and guidance for this method of customizing execution.