Custom Plugins

Hive Gateway is built with an highly modular architecture. The core is as small as possible, and most of the features are built around a plugin system.

A plugin can hook into each important steps of the lifecycle of the gateway, including the graphql request execution.

The gateway is built using Graphql Yoga (a GraphQL HTTP server) and Envelop (a GraphQL execution orchestrator), which are also highly modular technologies. Hive Gateway is extending Yoga and Envelop plugin system by providing gateway specific hooks.

This means that if a plugin exists in multiple variants, you should follow this priority order:

  1. Hive Gateway: is aware of subgraphs and upstream request management.
  2. Yoga: is aware of the HTTP transport. It most of the time provides optimisations over the Envelop variants.
  3. Envelop: is only aware of the GraphQL operation execution.

For example, the Prometheus integration plugin is available for Envelop (@envelop/prometheus), Yoga(@graphql-yoga/plugin-prometheus) and Hive Gateway(@graphql-mesh/prometheus). In this case, you should pick @graphql-mesh/prometheus plugin. It is built on top of Yoga and Envelop variants, but adds monitoring capabilities of upstream subgraphs.

For the most used features, you will not have to explicitly setup the dedicated plugin. They will be added automatically based on your gateway configuration.

Configuration

You can add plugins to your gateway using the plugins option.

It have to be a function, which will be called each time the gateway have update its configuration. For example, if polling is enabled, this function will be called for each poll.

Most Hive Gateway plugins takes an object as a parameter, and expect some common components like a logger, a pubsub, etc… Those components are given in parameters to the plugins function. It is advised to spread the plugin’s factory context into the plugins options, this way plugins will have access to all components they need.

gateway.config.ts
import { defineConfig } from '@graphql-hive/gateway'
import useNewRelic from '@graphql-mesh/plugin-newrelic'
import { useSOFA } from '@graphql-yoga/plugin-sofa'
 
export const gatewayConfig = defineConfig({
  plugins: ctx => [
    useSOFA({
      ...ctx,
      basePath: '/rest',
      swaggerUIEndpoint: '/rest/docs'
    }),
    useNewRelic({
      ...ctx
    })
  ]
})

Custom Plugins

You can take advantage of the plugin system to create your own custom plugin. This allows you to integrate your gateway with any technology that we don’t officially support, being public or private to your company.

A good entry-point for discovering how to write Gateway plugins is to look at the source code of the existing plugins maintained by us.

A plugin is an object that can define methods to hook into different phases or events of the gateway lifecycle. Each hook will receive a single parameter, the hook payload, containing data related to the hooked phase and functions allowing you to modify the behavior of this phase.

There are 2 categories of hooks:

  • Gateway lifecycle hooks: they are related to the lifecycle or events of the server itself and is not related to a specific request.
  • Request lifecycle hooks: they are tight to a specific phase of a specific request. Those hooks always give access to the related request in there payload.

Gateway lifecycle

Gateway lifecycle allows to setup long running services, initialize in-memory cache or stores, react to general events.

onYogaInit

It is the very first hook called just before the gateway starts listening for HTTP requests.

You can use this hook to customize the Yoga instance.

Payload fieldDescription
yogaYogaServer instance that is about to start with the gateway.

onPluginInit

Called before the server starts listening to HTTP requests.

In this hook, you can initialize every long running components, such as values stores, cache, third party clients, etc…

You can add other plugin in this hook, which allow you to ensure plugins you depends on are present.

Payload fieldDescription
addPlugin(plugin: Plugin)Allows adding a plugin to the plugin list. The new plugin will be added just after the one calling this function. It can be called multiple times, preserving plugin order.
plugins: Plugin[]The current list of plugins. It can be used to verify if a dependent plugin is already in the list or to ensure your plugin is not present twice. This should never be mutated; use addPlugin instead.
setSchema(schema: GraphQLSchema)Allows replacing the current schema.
registerContextErrorHandler(handler)Registers a handler that will be called if the GraphQL context factory throws an error. Useful for error reporting.

onSchemaChange

This hook is called when the schema changes, either because the Gateway loaded it the first time, or because a plugin changed it.

Possible usage examples of this hooks are:

  • Monitor changes of schemas
  • Analyse the schema or keep track of it for other hooks
  • Modify the schema
Payload fieldDescription
schemaThe new loaded schema
replaceSchemaReplace the schema. This will trigger a new call to onSchemaChange, so you should keep track of schema already modified by your plugin to avoid infinite change loop.

onFetch

This hook is called each time the fetch function is called. It can be either a request to an upstream subgraph using the http transport, or a request made a plugin to a third party technology.

This hook has a before and after stage. You can hook into the after phase by returning a function (see onParseDone).

Possible usage examples of the hooks are:

  • Manipulate HTTP Request object
  • Manipulate HTTP Response object
  • Change fetch implementation
  • Add custom headers
  • Monitor the HTTP request
Payload fieldDescription
urlThe target URL of the request
setUrlReplace the target URL
optionsThe fetch function options
setOptionsReplace the options that will be used to send the request
infoThe GraphQL operation info
fetchFnThe fetch function that will be used to make the request. By default, it is the one provided in the Gateway configuration if provided, or @whatwg-node/fetch if not.
setFetchFnReplace the fetch function that will be used to make the request. It should be compatible with standard fetch API.
executionRequestPresent only if the request is an upstream subgraph request. It contains all information about the upstream query, notably the target subgraph name.
requestIdA unique ID identifying the client request. This is used to correlate downstream and upstream requests across services.
loggerThe logger instance for the specific request that includes the details of the request and the response.
onFetchDone

Called once the fetch has ended and the response promise has been resolved.

Possible usage examples of the hook are:

  • Manipulate HTTP Response object.
  • Monitor the HTTP requests
Payload FieldDescription
responseThe response object returned by the fetch call.
setResponseFunction to replace the current response with a new response object.

onDispose

In order to clean up resources when Hive Gateway is shut down, you can use onDispose, Symbol.asyncDispose or Symbol.syncDispose to clean up resources.

You can learn more about Explicit Resource Management in Yoga documentation.

Request Lifecycle

The following diagram shows the lifecycle of request in Hive Gateway.

Each hook is given a single parameter called payload. Please see detail section of each hook to know what contains each of this payloads.

onRequest

This hook is invoked for ANY incoming HTTP request. Here you can manipulate the request or create a short circuit before Yoga handles the request.

⚠️

Exceptions thrown by this hook are not caught. This means they will buble up to the HTTP server underlying implementation.

For example, the node:http server crashes the entire process on uncaught exceptions.

Prefer onRequestParse when possible, or wrap the hook code in a try block.

For example, you can shortcut the manually handle an HTTP request, short-circuiting the Hive Gateway HTTP handler:

import type { GatewayPlugin } from '@graphql-hive/gateway'
 
function useAuth(): GatewayPlugin {
  return {
    onRequest({ request, fetchAPI, endResponse }) {
      if (!request.headers.get('authorization')) {
        endResponse(
          new fetchAPI.Response(null, {
            status: 401,
            headers: {
              'Content-Type': 'application/json'
            }
          })
        )
      }
    }
  }
}

Possible usage examples of this hook are:

  • Manipulate the request
  • Short circuit before Yoga handles the request
Payload fieldDescription
requestThe incoming HTTP request as WHATWG Request object. Learn more about the request.
serverContextThe early context object that is shared between all hooks and the GraphQL execution. Learn more about the context.
fetchAPIWHATWG Fetch API implementation. Learn more about the fetch API.
urlWHATWG URL object of the incoming request. Learn more about the URL object.
endResponseA function that allows you to end the request early and send a response to the client.

onRequestParse

This hook is invoked for any incoming GraphQL HTTP request and is invoked before attempting to parse the GraphQL parameters. Here you can manipulate the request, set a custom request parser or apply security measures such as checking for access tokens etc.

Possible usage examples of this hook are:

  • Manipulate the request
  • Set custom GraphQL request parser (use custom GraphQL protocol)
  • Apply security measures
Payload fieldDescription
requestThe incoming HTTP request as WHATWG Request object. Learn more about the request.
urlThe incoming HTTP request target url
requestParserThe function that will be used to extract GraphQL params from the request.
setRequestParserAllows to replace the function that will be used to extract GraphQL params from the request

onParams

This hook is invoked for an incoming GraphQL request after the GraphQL parameters (query, variables, extensions and operationName) have been ATTEMPTED to be parsed.

Within this hook you can manipulate and customize the parameters or even implement a whole new way of parsing the parameters (if you wish to diverge from the GraphQL over HTTP specification).

In addition to that you could also short-circuit and skip the GraphQL execution. E.g. you might want to serve a result from the cache instead.

Possible usage examples of this hook are:

  • Response Cache: Short-circuit GraphQL execution if response can be served from the cache.
  • Persisted Operations: Load the query document string from the persisted operations store before running the execution.
  • APQ: Load/persist the query document string on the persisted operations store.
Payload fieldDescription
requestThe incoming HTTP request as WHATWG Request object. Learn more about the request.
paramsThe GraphQLParams extracted from the request.
setParamsAllows to replace the GraphQLParams for this request
fetchAPIFetch API provided in the Hive Gateway configuration. This should be used to make outgoing HTTP request, since it will trigger the onFetch hook.
contextThe server context. It can contain runtime specific data, such as req and res object when running in Node for example.

onEnveloped

This hook is called once per operation, at the start of the GraphQL execution pipeline. This is useful if you need to do some setup right before an incoming execution flow.

Payload FieldDescription
setSchemaSets the schema to be used.
contextThe GraphQL context object.
extendContextExtends the GraphQL context object with additional fields.

onParse

Called for parsing the GraphQL document. This hook has a before and after stage. You can hook into the after phase by returning a function (see onParseDone).

Example actions in this hook:

  • Replace GraphQL parser and support future GraphQL syntax
  • Collect metrics about parsing success/failures
Payload FieldDescription
contextThe current context object.
extendContextExtend the context object with a partial.
paramsThe parameters that are passed to the parse call.
parseFnThe current parse function.
setParseFnReplace the current parse function.
setParsedDocumentSet/overwrite the parsed document. If a parsed document is set the call to the parseFn will be skipped.
onParseDone

You can return a function from onParse to register a hook called after the parsing of the GraphQL document.

Example actions in this hook:

  • Modify the parsed document
  • Gather metadata from the document, like directives.
Field NameDescription
contextThe GraphQL context object.
extendContextExtend the GraphQL context object with a partial.
resultThe result of the parse phase.
replaceParseResultReplace the parse result with a new result.

onValidate

Called for validating the GraphQL document. This hook has a before and after stage. You can hook into the after phase by returning a function (see onValidateDone)

Example actions in this hook:

  • Register additional validation rules (e.g. Disable Introspection)
  • Collect metrics about validation success/failures
Payload fieldDescription
contextThe current context object.
extendContextExtend the context object with a partial.
paramsThe parameters with which the validate function will be invoked.
addValidationRuleRegister a validation rule that will be used for the validate invocation.
validateFnThe current validate function that will be invoked.
setValidationFnOverwrite the current validate function.
setResultSet a validation error result and skip the validate invocation.
onValidateDone

You can return a function from onValidate to register a hook called after the validation of the GraphQL operation.

Example actions in this hook:

  • Replace the validation result
  • Collect metrics and validation errors
Field NameDescription
contextThe current context object.
extendContextExtend the context object with a partial.
validWhether the validation raised any errors or not.
resultAn array of errors that were raised during the validation phase.
setResultReplace the current error result with a new one.

onContextBuilding

Called for building the GraphQL context. This hook has a before and after stage. You can hook into the after phase by returning a function (see onValidateDone)

Example actions in this hook:

  • Authentication
  • Add data to context object (e.g. user data or dataloader instances)
Payload fieldDescription
contextThe current GraphQL context object.
extendContextExtend the context object with a partial.
breakContextBuildingPrevent calls on any further context building hooks.
onContextBuildingDone

You can return a function from onValidate to register a hook called after the construction of the GraphQL context.

Example actions in this hook:

  • Modify the context
Field NameDescription
contextThe GraphQL context object.
extendContextExtend the GraphQL context object with a partial.

onExecute

Called for executing a GraphQL mutation or query operation.

This hook has a before and after stage. You can hook into before and after the GraphQL request is executed by returning an object containing an onExecuteDone method (see onExecuteDone).

Example actions in this hook:

  • Collect metrics about execution time
  • Error logging/reporting
Payload fieldDescription
executeFnCurrent execute function that will be used for execution.
argsArguments the execute function will be invoked with.
setExecuteFnReplace the current execute function with a new one.
setResultAndStopExecutionSet an execution result and skip calling the execute function.
extendContextExtend the context object with a partial.
onExecuteDone

To register this hook, you need to return an object from onExecute hook.

If the result is an AsyncIterable, you can hook into incremental result delivery by returning an object with onNext and onEnd methods (see AsyncIterable results hooks).

my-plugin.ts
const useMyPlugin() = () => ({
  onExecute: () => ({
    onExecuteDone: payload => {
      // handle the end of the execution here
    }
  })
})

This plugin is called once the execution is step is finished and a result has been computed.

Example actions in this hook:

  • Error logging/reporting
  • Result modification
Payload FieldDescription
argsThe execution arguments.
resultThe execution result returned from the execute function. Can return an AsyncIterable if a graphql.js that has defer/stream implemented is used.
setResultReplace the execution result with a new execution result.

onSubscribe

Called for subscribing to a GraphQL subscription operation. This hook has a before and after stage. You can hook into before and after the GraphQL subscription is executed.

Example actions in this hook:

  • Collect metrics about execution time
  • Error logging/reporting
Payload fieldDescription
subscribeFnCurrent subscribe function that will be used for setting up the subscription.
argsCurrent arguments with which the subscribe function will be invoked.
setSubscribeFnReplace the current subscribe function with a new one that will be used for setting up the subscription.
extendContextExtend the context object with a partial.
setResultAndStopExecutionSet a subscribe result and skip calling the subscribe function.
onSubscriptionResult

Called when the AsyncIterable result have been created and is ready to produce incremental results.

It is possible to hook into the delivery of each incremental result and the end of the stream by returning an object with onNext and onEnd methods (see AsyncIterable results)

Payload FieldDescription
argsThe execution arguments.
resultThe current incremental execution result.
setResultReplace the current execution result with a new execution result.
onSubscriptionError

Called for the first exception thrown by the AsyncIterable.

Payload FieldDescription
errorThe error that was thrown.
setErrorFunction to replace the error.

AsyncIterable results

When a result is an AsyncIterable (which happens when using defer, stream or subscriptions), it is possible to hook into the delivery of each intermediate results and the end of the stream.

To hook into those phases, return an object with onNext and/or onEnd methods from onExecuteDone or onSubscriptionResult hooks.

plugin.ts
export const useMyPlugin = () => ({
  // For queries and mutations (defer and stream)
  onExecute: () => ({
    onExecuteDone: () => ({
      onNext: () => ...,
      onEnd: () => ...,
    })
  })
 
  // For subscriptions
  onSubscribe: () => ({
    onSubscriptionResult: () => ({
      onNext: () => ...,
      onEnd: () => ...,
    })
  })
})
onNext

This hook is called each time a new intermediate result is produced by the AsyncIterable, before it is sent to the client.

Field NameDescription
argsThe execution arguments.
resultThe execution result.
setResultReplace the execution result with a new execution result.
onEnd

This hook is called at when the AsyncIterable has ended, just before notifying the client.

There is no payload provided for this hook. It is advised to use the payload of onExecuteDone or onSubscriptionResult to have access to the GraphQL context or other data related to the request.

onDelegationPlan

Called each time a delegation plan is calculated for a query. It is called for each nested selection set level of the query.

This hook is not called for each queries, because the plan is cached. This means it will be called only the first time a query document is encountered since the last schema change.

This hook has a before and after stage. You can return a function to hook into the after stage (see onDelegationPlanDone).

This hook is mostly used for monitoring and tracing purposes.

Field NameDescription
supergraphThe GraphQL schema of the supergraph.
subgraphThe name of the subgraph.
sourceSubschemaThe schema of the subgraph.
typeNameThe name of the type being planed.
variablesThe variables provided in the client request.
fragmentsThe fragments provided in the client request.
fieldNodesThe field nodes of selection set being planned.
contextThe GraphQL context object.
requestIdA unique ID identifying the client request. This is used to correlate downstream and upstream requests across services.
loggerThe logger instance for the specific request that includes the details of the request and the response.
infoThe GraphQLResolveInfo object of the client query.
delegationPlanBuilderThe delegation plan builder.
setDelegationPlanBuilderFunction to replace the current delegation plan builder.
onDelegationPlanDone

Called when a delegation plan has been calculated.

This hook is mostly used for monitoring and tracing purpose.

Payload FieldDescription
delegationPlanThe delegation plan calculated.
setDelegationPlanFunction to replace the current delegation plan with a new delegation plan.

onDelegationStageExecute

Called when a delegation plan stage is executed. This can be called multiple times for a query if it requires multiple stages to be fully resolved.

This hooks has a before and after stage. You can return a function to hook into the after stage (see onDelegationStageExecuteDone).

Payload FieldDescription
objectThe object being resolved.
contextThe GraphQL context object.
infoThe GraphQLResolveInfo object.
subgraphThe name of the subgraph.
subschemaThe schema of the current subgraph.
selectionSetThe selection set node that will be queried.
keyThe key for the entity being resolved.
typeThe type of the entity being resolved.
typeNameThe name of the type being resolved.
resolverThe resolver function for the merged type.
setResolverFunction to set a new resolver for the merged type.
requestIdA unique ID identifying the client request. This is used to correlate downstream and upstream requests across services.
loggerThe logger instance for the specific request that includes the details of the request and the response.
onDelegationStageExecuteDone

Called when the delegation plan has been executed and all subgraph queries have been done for the current stage.

Field NameDescription
resultThe result of the delegation execution.
setResultFunction to replace the result.

onSubgraphExecute

This hook is invoked for ANY request that is sent to the subgraph.

Example actions in this hook:

  • Manipulate the request
  • Add a custom auth header
  • Monitor the subgraph request

You can see Prometheus plugin for an example of how to use this hook.

Payload FieldDescription
subgraphThe GraphQL schema of the subgraph.
subgraphNameThe name of the subgraph.
transportEntryThe transport entry that will be used to resolve queries for this subgraph
executionRequestThe execution request object containing details of the upstream GraphQL operation.
setExecutionRequestFunction to replace the current execution request.
executorThe executor function used to execute the upstream request.
setExecutorFunction to replace the current executor.
requestIdA unique ID identifying the client request.
loggerThe logger instance for the specific request that includes the details of the request and the response.
onSubgraphExecuteDone

Called when the subgraph execution is done and a result has been received.

Payload FieldDescription
resultThe result of the subgraph execution, which can be an ExecutionResult or an AsyncIterable<ExecutionResult>.
setResultFunction to replace the result.

onExecutionResult

This hook is invoked for each result produced for GraphQL operation, before it is processed to be sent to client.

In particular, it is useful to handle batched operations. If a request contains batched operations, this hook is called once of each operation, while onResultProcess will be only called once for the entire request.

Here, you can modify the result, to add monitoring or instrumentation extensions for example.

Example actions in this hook:

  • Add metadata to results
  • Collect errors
Payload fieldDescription
requestThe incoming HTTP request as WHATWG Request object. Learn more about the request.
resultThe execution result, which can be an ExecutionResultWithSerializer, an AsyncIterable<ExecutionResultWithSerializer>.
setResultAllows to replace the execution result.
contextThe GraphQL context.

onResultProcess

This hook is invoked after a GraphQL request has been processed and before the response is forwarded to the client. Here you can customize what transport/response processor format should be used for sending the result over the wire.

Example actions in this hook:

  • Specify custom response format
  • Logging/Metrics
Field NameDescription
requestThe incoming HTTP request as WHATWG Request object.
resultThe execution result, which can be an ExecutionResultWithSerializer, an AsyncIterable<ExecutionResultWithSerializer>. If the request contains batch queries, this will be an array
setResultAllows to replace the execution result.
resultProcessorThe result processor to be used to turn the result into an HTTP Response object.
acceptableMediaTypesList of acceptable media types for the result processor.
setResultProcessorAllows to set the result processor and the accepted media type.
serverContextThe server context which can contain runtime specific data, such as for example req and res object when running in Node

onResponse

This hook is invoked after a HTTP request (both GraphQL and NON GraphQL) has been processed and after the response has been forwarded to the client. Here you can perform any cleanup or logging operations, or you can manipulate the outgoing response object.

⚠️

Exceptions thrown by this hook are not caught. This means they will buble up to the HTTP server underlying implementation.

For example, the node:http server crashes the entire process on uncaught exceptions.

Prefer onRequestParse when possible, or wrap the hook code in a try block.

Example actions in this hook:

  • Specify custom response format
  • Logging/Metrics
Field NameDescription
requestThe incoming HTTP request as WHATWG Request object. Learn more about the request.
serverContextThe final context object that is shared between all hooks and the GraphQL execution. Learn more about the context.
responseThe outgoing HTTP response as WHATWG Response object. Learn more about the response interface.

Plugin Context

Hive Gateway comes with ready-to-use logger, fetch, cache storage and etc that are shared across different components. We’d highly recommend you to use those available context values instead of creating your own for a specific plugin.

gateway.config.ts
import { defineConfig } from '@graphql-hive/gateway'
 
export const gatewayConfig = defineConfig({
  plugins({
    fetch, // WHATWG compatible Fetch implementation.
    logger, // Logger instance used by Hive Gateway
    cwd, // Current working directory
    pubsub, // PubSub instance used by Hive Gateway
    cache // Cache storage used by Hive Gateway
  }) {
    return [
      useMyPlugin({ logger, fetch }) // So the plugin can use the shared logger and fetch
    ]
  }
})