Migrating from v2 to v3

Migrating from v2 to v3

With new major version comes breaking changes. This section will guide you through the process of migrating from v2 to v3.

1. Remove graphql as a peer dependency

We have designed the new envelop to be engine agnostic. This means that graphql is no longer a peer dependency. You still want to use graphql-js but you can easily use any GraphQL Engine since envelop now simply wraps the parse, validate, execute and subscribe functions that you provide.

- import { envelop } from '@envelop/core';
+ import { envelop, useEngine } from '@envelop/core';
+ import * as GraphQLJS from 'graphql'
- const getEnveloped = envelop([ ... ])
+ const getEnveloped = envelop({ plugins: [ useEngine(GraphQLJS), ... ] })

2. Removed orchestrator tracing

GraphQLSchema was wrapped to provide resolvers/fields tracing from the schema. Issue with this approach was it was very specific to the underlying engine’s implementation. With the new version we no longer want to depend to a specific implementation. Now users can wrap their schemas and add tracing themselves.

1. Removed onResolverCalled

We decided to drop onResolverCalled hook and instead provide a new plugin that will let you hook into this phase.

import { parse, validate, specifiedRules,execute, subscribe } from 'graphql'
import { envelop, Plugin, useEngine } from '@envelop/core'
+ import { useOnResolve } from '@envelop/on-resolve'
import { onResolverCalled } from './my-resolver'
function useResolve(): Plugin {
  return {
-   onResolverCalled: onResolverCalled,
+   onPluginInit: ({ addPlugin }) => {
+     addPlugin(useOnResolve(onResolverCalled))
+   },
const getEnveloped = envelop({
  plugins: [
    useEngine({ parse, validate,specifiedRules, execute, subscribe }),
    // ... other plugins ...

2. Drop useTiming plugin

This plugin was dependent on tracing the schema. As we no longer support wrap the schema out of the box we decided to drop this plugin.

At this moment we do not have any alternative. We recommend using a tracing solution.

3. Remove EnvelopError

To keep the core agnostic from a specific implementation we no longer provide the EnvelopError class. To ensure an error is GraphQLError envelop check if it an instanceOf Error and the name of error is GraphQLError. We provide a helper utility isGraphQLError to check if an error is a GraphQLError.

- import {EnvelopError} from '@envelop/core'
+ import { GraphQLError } from 'graphql'
const main () => {
  try {
    // ...
  } catch (error) {
- throw EnvelopError(error)
+ throw GraphQLError(error)

1.@envelop/sentry default will skip all GraphQLError

Since we no longer support EnvelopError it will skip sending any errors to sentry that are GraphQLError.

4. Remove isIntrospectionDocument and isIntrospectionQuery utility

To keep core agnostic we recommend you create your own utility for this.

5. Remove useAsyncSchema plugin

This was a mistake from beginning as we cannot asynchronously validate and parse since with graphql these functions are synchronous in nature.

You should first load your schema and then create the envelop instance and pass the schema to it.

import * as GraphQLJS from 'graphql'
import { envelop, useEngine, useSchema } from '@envelop/core'
// This assumes you are using Node v14.8+ where top-level await is supported
const schema = await loadSchema()
const getEnveloped = envelop({
  plugins: [useEngine(GraphQLJS), useSchema(schema)]

6. Rename useLazyLoadedSchema to useSchemaByContext

Original name was very misleading since lazy loading could mean it can be asynchronous in nature. This plugin was renamed to better reflect its purpose. It is now called useSchemaByContext

7. Drop enableIf utility

This utility was used to enable plugins conditionally. For a better developer experience we have dropped this utility and favor more type safe way to conditionally enable plugins.

- import { envelop, useMaskedErrors, enableIf } from '@envelop/core'
+ import { envelop, useMaskedErrors, useEngine } from '@envelop/core'
import * as GraphQLJS from 'graphql'
const isProd = process.env.NODE_ENV === 'production'
const getEnveloped = envelop({
 plugins: [
  // This plugin is enabled only in production
-  enableIf(isProd, useMaskedErrors())
+  isProd && useMaskedErrors()

8. Update options for useMaskedErrors plugin

  • We removed handleValidationErrors and handleParseErrors options since ONLY masking validation errors OR ONLY disabling introspection errors does not make sense, as both can be abused for reverse-engineering the GraphQL schema (see https://github.com/nikitastupin/clairvoyance for reverse-engineering the schema based on validation error suggestions). Instead you should use useErrorHandler plugin where you can access each phase and decide what to do with the error.
  • Renamed formatError to maskError
import { envelop, useMaskedErrors, useEngine, isGraphQLError } from '@envelop/core'
import * as GraphQLJS from 'graphql'
export const customFormatError: MaskError = err => {
  if (isGraphQLError(err)) {
    return new GraphQLError('Sorry, something went wrong.')
  return err
const getEnveloped = envelop({
  plugins: [
-   useMaskedErrors({ formatError: customFormatError })
+   useMaskedErrors({ maskError: customFormatError })

9. Drop support for Node.js v12

Node.js v12 is no longer supported by the Node.js team. https://github.com/nodejs/Release/#end-of-life-releases