v2
Plugins
Lifecycle

Plugin Lifecycle

Plugins are executed in order of their usage, and inject functionality serially, so aim to keep your plugins simple and standalone as much as possible.

The before functions are called in order, and then the after function of each hook will be called in the same order when the phase has been done.

The basic API allows you to keep the JS closure between before and after, for example:

const myPlugin = {
  onParse({ params }) {
    // This is the before function
 
    return ({ result }) => {
      // The is the after function,
      // JS Closure allow me to access the params here as well
    }
  }
}
💡

All plugin lifecycle methods are executed in LIFO order (Last-in, First-out) to make timing more consistent.

The Improved context

While in regular GraphQL executions, the context is built right before execute happens, in envelop we allow plugin developers to take part it that context from all phases.

While calling getEnveloped function (the result of envelop({ plugins: [ ... ]})), you can pass any custom object that will be the base for your GraphQL execution context.

In most cases, you’ll pass the incoming HTTP request (or, just the relevant parts of it) to make it available for the plugins you use:

import * as GraphQLJS from 'graphql'
import { envelop, useEngine } from '@envelop/core'
 
const getEnveloped = envelop({
  plugins: [
    useEngine(GraphQLJS)
    // ... plugins
  ]
})
 
myHttpServer.on('request', async req => {
  const { parse, validate, contextFactory, execute, schema } = getEnveloped({ req })
  // ...
})

Plugins might also use this context to keep a contextual reference for objects they might need across stages.

So for example, if you wish to make some data from parse stage available for you in execute phase, you can extend the context while running onParse and then access that data in onExecute:

const myPlugin = {
  onParse({ extendContext }) {
    extendContext({
      myVar: 'test'
    })
  },
  onExecute({ args }) {
    const myVar = args.contextValue.myVar
  }
}

Plugins API

You can find the complete signature for plugins API here.

onPluginInit(api)

This method is called only once when the plugin is being initialized. This hook is triggered only once and is useful for setting up the plugin.

API

  • setSchema - sets the initial schema to be used.
  • plugins - list of all other loaded plugins.
  • addPlugin - adds a plugin to the list of plugins.

onEnveloped(api)

This method is called every time getEnveloped is called, and per request. This is useful if you need to do some setup right before an incoming execution flow.

API

  • setSchema - sets the initial schema to be used.
  • context - the initial context object passed to getEnveloped, including the context built by plugins that ran before.
  • extendContext - extends the initial context object with additional fields.

onParse(api)

Called every time an operation is being executed.

before

  • params - the original parameters passes to parse function.
  • parseFn - the current parse function. By default, it’s the one from graphql package.
  • setParseFn - Replaces the parse function with a custom function.
  • setParsedDocument - sets the parse result. Setting this will skip calling parse for the executed operation.
  • context - the context object built so far by other plugins.
  • extendContext - extends the context object with additional fields.

after

  • result - the result DocumentNode of the parsing function.
  • replaceParseResult - replaces the parsed result.
  • context - the context object built so far by other plugins.
  • extendContext - extends the context object with additional fields.

onValidate(api)

Called every time an operation is being executed.

before

  • params - the original parameters passes to validate function.
  • validateFn - the current validate function. By default, it’s the one from graphql package.
  • setValidationFn - Replaces the validate function with a custom function.
  • addValidationRule - Adds a validation rule to the list of default validation rules (as defined in graphql package).
  • setResult - sets the validation result. Setting this will skip calling validate for the executed operation.
  • context - the context object built so far by other plugins.
  • extendContext - extends the context object with additional fields.

after

  • valid - A boolean indicates if the validation passed.
  • result - null in case of a valid document, otherwise an array of validation errors.
  • context - the context object built so far by other plugins.
  • extendContext - extends the context object with additional fields.

onContext(api)

Called every time an operation is being executed. Used for building the GraphQL context incrementally.

before

  • context - the context object built so far by other plugins.
  • extendContext - extends the context object with additional fields.

after

  • context - the eventual built context, by all plugins.
  • extendContext - extends the context object with additional fields.

onExecute(api)

Called every time an operation is being executed. The return value of this function is extended and allows you to hook into resolver calls if needed.

before

  • args - arguments pass to execute (contains the document, variables and everything else that is needed for executing the GraphQL operation)
  • executeFn - the execute function to use, by default it’s the one from graphql package.
  • setExecuteFn - replaces the execute function.
  • setResultAndStopExecution - sets the result of the execution immediately. Calling this function will stop execution.
  • extendContext - allow you to extend the context before executing the operation.

You can return an object from that function, with the following fields:

onExecuteDone

Triggered when the execution of the operation is done.

  • result - the execution result, or AsyncIterable in case of stream response.
  • setResult - replaces the result. can either be with data or errors.

Since envelop aims to support stream responses (for live queries, or @stream/@defer), the result might be an AsyncIterable of multiple execution results.

If you wish your plugin to support this, please make sure to use the handleStreamOrSingleExecutionResult helper, like that:

import { handleStreamOrSingleExecutionResult, Plugin } from '@envelop/types'
 
const myPlugin = (): Plugin => {
  return {
    onExecute({ args }) {
      return {
        onExecuteDone(payload) {
          return handleStreamOrSingleExecutionResult(payload, ({ result, setResult }) => {
            // Here you can access the result, and modify it with setResult if needed
          })
        }
      }
    }
  }
}

Alternately, if you don’t need to support stream responses, you can use the isAsyncIterable function as a type-guard:

import { isAsyncIterable, Plugin } from '@envelop/types'
 
const myPlugin = (): Plugin => {
  return {
    onExecute({ args }) {
      return {
        onExecuteDone({ result, setResult }) {
          if (!isAsyncIterable(result)) return
          // Here you can access result, and modify it with setResult if needed
        }
      }
    }
  }
}

onSubscribe(api)

Called every time a subscription operation is being executed. The return value of this function is extended, and allow you to hooks into resolver calls if needed.

before

  • args - arguments passes to subscribe (contains the document, variables and everything else that needed for executing the GraphQL operation)
  • subscribeFn - the subscribe function to use, by default it’s the one from graphql package.
  • setSubscribeFn - replaces the subscribe function.
  • extendContext - allow you to extend the context before executing the operation.
  • setResultAndStopExecution - sets the result of the execution immediately. Calling this function will stop execution.

You can return an object from that function, with the following fields:

onSubscribeResult

Triggered when subscription result is being emitted from a subscription execution.

  • result - the subscription result.
  • setResult - replaces the result. can either be with data or errors.

onSchemaChange(api)

envelop allow you to manage a reference to a schema, that you can later access and use within your server.

Some plugins (like gateway implementations) could potentially change the schema while running, so envelop will trigger that event in case of a schema change after all plugins have initialized.

API

  • schema - the GraphQLSchema
  • replaceSchema - replaces the schema. Calling this will trigger onSchemaChange for all other plugins (except for the one that initiated the change);