DocumentationGraphQL Harness

GraphQL Harness

GraphQLHarness is new in GraphQL.js v17. It customizes the phases used by graphql() and graphqlSync().

graphql() is the convenience entry point that parses, validates, and executes a GraphQL operation. In v17, those phases are represented by a harness:

type GraphQLHarness = {
  parse: GraphQLParseFn;
  validate: GraphQLValidateFn;
  execute: GraphQLExecuteFn;
  subscribe: GraphQLSubscribeFn;
};

defaultHarness is the built-in harness used by graphql() and graphqlSync().

import { defaultHarness, graphql } from 'graphql';
 
const result = await graphql({
  schema,
  source,
  harness: defaultHarness,
});

Why this exists

The harness is a host integration API. It exists to demonstrate how GraphQL.js can be customized along the same lines as Envelop, The Guild’s GraphQL plugin system, and to provide type infrastructure that supports the broadest community of framework, server, plugin, and runtime authors. One example is GraphQLParseFn: it can return a DocumentNode or a promise for a DocumentNode, even though the built-in GraphQL.js parse() function is synchronous.

GraphQL.js names that type shape PromiseOrValue; Envelop commonly calls the same shape MaybePromise.

For application servers, prefer Envelop or a framework built on Envelop over using a raw GraphQLHarness directly. The harness is useful when a library or runtime already owns the request pipeline and needs GraphQL.js to expose typed parse, validation, execution, or subscription phases.

What can be customized

Each phase has the same call shape as the corresponding GraphQL.js function:

type GraphQLParseFn = typeof parse;
type GraphQLValidateFn = typeof validate;
type GraphQLExecuteFn = typeof execute;
type GraphQLSubscribeFn = typeof subscribe;

The parse and validate phases may return synchronously or asynchronously. graphqlSync() still requires every phase and resolver it reaches to complete synchronously.

Cached documents

A host that has a trusted document cache can replace the parse phase while keeping the default validation and execution behavior.

import { defaultHarness, graphql } from 'graphql';
 
const harness = {
  ...defaultHarness,
  parse(source, options) {
    const cached = documents.get(String(source));
    return cached ?? defaultHarness.parse(source, options);
  },
};
 
const result = await graphql({
  schema,
  source,
  variableValues,
  operationName,
  harness,
});

External validation

A host can also replace validation. This is useful for persisted operation registries that validate at build time and return stored validation results at runtime.

import { defaultHarness, graphql } from 'graphql';
 
const harness = {
  ...defaultHarness,
  async validate(schema, document, rules, options) {
    const cached = await registry.getValidationResult(document, schema);
    return cached ?? defaultHarness.validate(schema, document, rules, options);
  },
};
 
const result = await graphql({
  schema,
  source,
  harness,
});

Relationship to incremental delivery

graphql() remains a single-result operation pipeline. A harness does not make graphql() return incremental delivery payloads.

Operations that use @defer or @stream should use experimentalExecuteIncrementally() after parsing and validation. See Advanced Execution Pipelines for the lower-level execution APIs and Defer and Stream for the incremental result shape.