github ardatan/graphql-tools release-1793020434000
October 26, 2026

2 hours ago

@graphql-tools/jest-transform@3.1.0

Minor Changes

  • #8605 ea2603a Thanks @ardatan! - @graphql-tools/webpack-loader (and @graphql-tools/jest-transform, which calls it) now accepts more than one GraphQL operation in the same file and exports each named operation, matching graphql-tag/loader.

    A file with two mutations used to throw GraphQL Webpack Loader allows only for one GraphQL Operation per file, so this import failed in webpack and Jest:

    # menuMutations.graphql
    mutation deleteMenuMutation {
      deleteMenu {
        ...MenuFields
      }
    }
    
    mutation upsertMenuMutation {
      upsertMenu {
        id
      }
    }
    
    fragment MenuFields on Menu {
      id
      name
    }
    import { deleteMenuMutation, upsertMenuMutation } from './menuMutations.graphql'

    The default export is still the whole document. Each named export is that operation plus the fragments it uses. deleteMenuMutation includes MenuFields; upsertMenuMutation does not.

    import menuMutations, {
      deleteMenuMutation,
      upsertMenuMutation,
    } from './menuMutations.graphql'

    A single named operation is also exported under its name. Unnamed operations are still allowed when the file has only one operation. If the file has more than one operation, each operation must be named.

    jest-transform now passes the Jest source path and filesystem into the loader, so esModule: true can emit named exports for operations and fragments pulled in with #import. CommonJS named exports use an own property so a GraphQL name of __proto__ does not replace the export object's prototype.

Patch Changes

  • Updated dependencies [ea2603a]:
    • @graphql-tools/webpack-loader@8.1.0

@graphql-tools/load@9.1.0

Minor Changes

  • #8603 62ef681 Thanks @ardatan! - Custom loaders on loadDocuments / loadDocumentsSync / loadSchema can now return an array. Each item is added as its own source.

    A pointer such as src/**/*.{ts,tsx} calls the custom loader once. Returning a single combined document kept every operation on that one pointer. Returning an array was ignored, so the load failed with Couldn't find any GraphQL type definitions.

    // custom-loader.js
    const { parse } = require('graphql');
    
    module.exports = function () {
      return [
        {
          document: parse(/* GraphQL */ `
            query Users {
              users {
                id
              }
            }
          `),
          location: 'users.graphql',
        },
        {
          document: parse(/* GraphQL */ `
            query Orders {
              orders {
                id
              }
            }
          `),
          location: 'orders.graphql',
        },
      ];
    };
    loadDocuments(
      {
        'src/**/*.{ts,tsx}': {
          loader: './custom-loader.js',
        },
      },
      { loaders: [] },
    );

    The result is two sources: Users at users.graphql and Orders at orders.graphql. A single DocumentNode or { document } return value still becomes one source. An array of DocumentNodes uses the pointer as each item's location.

  • #8606 6da2904 Thanks @ardatan! - loadDocuments / loadTypedefs now accept errorHandling so a single invalid file does not have to hide the rest of the load.

    A glob that mixes a valid operation with broken SDL used to throw the first Syntax Error and drop every other file:

    # valid.query.graphql
    query ValidQuery {
      a
    }
    # broken-a.query.graphql
    query {
      a
    await loadDocuments('**/*.query.graphql', {
      loaders: [new GraphQLFileLoader()],
    });
    // throws Syntax Error

    errorHandling chooses what happens next. The default is unchanged (throw the first error).

    aggregate waits until every source is attempted, then throws AggregateError with each parse/load failure:

    await loadDocuments('**/*.query.graphql', {
      loaders: [new GraphQLFileLoader()],
      errorHandling: 'aggregate',
    });

    return does not throw. Failed sources stay in the result as { location, error } without a document, next to the sources that parsed:

    const sources = await loadDocuments('**/*.query.graphql', {
      loaders: [new GraphQLFileLoader(), new CodeFileLoader()],
      errorHandling: 'return',
    });
    
    sources[0].document; // ValidQuery
    sources[1].document; // undefined
    sources[1].error; // GraphQLError: Syntax Error

    Source now has an optional error field. @graphql-tools/graphql-file-loader and @graphql-tools/code-file-loader honor the same option. CodeFileLoader's existing noSilentErrors default is unchanged unless errorHandling is set.

    Custom loader failures and incomplete inline documents (query Foo {) follow the same option. errorHandling: 'throw' raises the first failure, not AggregateError.

  • #8602 46039a0 Thanks @ardatan! - loadSchema and loadSchemaSync already accept sort: true. That always runs GraphQL's lexicographicSortSchema, which reorders the type list and also reorders fields, arguments, and enum values.

    A schema that was written with a meaningful enum or field order cannot keep that order after load:

    # loaded schema
    enum DayOfWeek {
      SUNDAY
      MONDAY
      TUESDAY
      WEDNESDAY
      THURSDAY
      FRIDAY
      SATURDAY
    }
    
    type Query {
      d: String
      z: String
      a: String
      day: DayOfWeek
    }
    
    type User {
      aa: String
      a: String
    }
    
    type A {
      s: String
      b: String
    }
    # sort: true (current default)
    type A {
      b: String
      s: String
    }
    
    enum DayOfWeek {
      FRIDAY
      MONDAY
      SATURDAY
      SUNDAY
      THURSDAY
      TUESDAY
      WEDNESDAY
    }
    
    type Query {
      a: String
      d: String
      day: DayOfWeek
      z: String
    }
    
    type User {
      a: String
      aa: String
    }

    This release adds sortMode on LoadSchemaOptions.

    • sort: true with no sortMode, or sortMode: 'lexicographic', keeps the current behavior.
    • sort: true, sortMode: 'typesOnly' sorts only the type list. Field and enum value order stay as they were loaded. If the same type is split across files, those members keep merge order instead of being sorted.
    • sort: false (or omitted) ignores sortMode.
    loadSchema('schema.graphql', {
      loaders: [new GraphQLFileLoader()],
      sort: true,
      sortMode: 'typesOnly',
    });
    # sort: true, sortMode: 'typesOnly'
    type A {
      s: String
      b: String
    }
    
    enum DayOfWeek {
      SUNDAY
      MONDAY
      TUESDAY
      WEDNESDAY
      THURSDAY
      FRIDAY
      SATURDAY
    }
    
    type Query {
      d: String
      z: String
      a: String
      day: DayOfWeek
    }
    
    type User {
      aa: String
      a: String
    }

Patch Changes

  • #8604 9df67b4 Thanks @ardatan! - useCustomLoader and useCustomLoaderSync now include the original load failure in the thrown error message.

    A missing, broken, or non-function custom loader used to throw only:

    Failed to load custom loader: ./codegen-loader.js
    

    The underlying SyntaxError, Cannot find module, or "did not export a function" was swallowed, so codegen and loadDocuments could not show why the file failed.

    // broken-loader.js
    module.exports = function ( {
    await useCustomLoader('./broken-loader.js', cwd);
    Failed to load custom loader: ./broken-loader.js
    SyntaxError: Unexpected token '{'
    

    getCustomLoaderByPath still returns null when the file cannot be loaded.

  • Updated dependencies [6da2904, 902d870, 7770067, f956bfe]:

    • @graphql-tools/utils@13.2.0
    • @graphql-tools/schema@11.0.7

@graphql-tools/code-file-loader@9.1.0

Minor Changes

  • #8606 6da2904 Thanks @ardatan! - loadDocuments / loadTypedefs now accept errorHandling so a single invalid file does not have to hide the rest of the load.

    A glob that mixes a valid operation with broken SDL used to throw the first Syntax Error and drop every other file:

    # valid.query.graphql
    query ValidQuery {
      a
    }
    # broken-a.query.graphql
    query {
      a
    await loadDocuments('**/*.query.graphql', {
      loaders: [new GraphQLFileLoader()],
    });
    // throws Syntax Error

    errorHandling chooses what happens next. The default is unchanged (throw the first error).

    aggregate waits until every source is attempted, then throws AggregateError with each parse/load failure:

    await loadDocuments('**/*.query.graphql', {
      loaders: [new GraphQLFileLoader()],
      errorHandling: 'aggregate',
    });

    return does not throw. Failed sources stay in the result as { location, error } without a document, next to the sources that parsed:

    const sources = await loadDocuments('**/*.query.graphql', {
      loaders: [new GraphQLFileLoader(), new CodeFileLoader()],
      errorHandling: 'return',
    });
    
    sources[0].document; // ValidQuery
    sources[1].document; // undefined
    sources[1].error; // GraphQLError: Syntax Error

    Source now has an optional error field. @graphql-tools/graphql-file-loader and @graphql-tools/code-file-loader honor the same option. CodeFileLoader's existing noSilentErrors default is unchanged unless errorHandling is set.

    Custom loader failures and incomplete inline documents (query Foo {) follow the same option. errorHandling: 'throw' raises the first failure, not AggregateError.

Patch Changes

  • Updated dependencies [6da2904, 7770067, f956bfe]:
    • @graphql-tools/utils@13.2.0
    • @graphql-tools/graphql-tag-pluck@9.0.8
    • @graphql-tools/load-files@8.0.7

@graphql-tools/graphql-file-loader@9.1.0

Minor Changes

  • #8606 6da2904 Thanks @ardatan! - loadDocuments / loadTypedefs now accept errorHandling so a single invalid file does not have to hide the rest of the load.

    A glob that mixes a valid operation with broken SDL used to throw the first Syntax Error and drop every other file:

    # valid.query.graphql
    query ValidQuery {
      a
    }
    # broken-a.query.graphql
    query {
      a
    await loadDocuments('**/*.query.graphql', {
      loaders: [new GraphQLFileLoader()],
    });
    // throws Syntax Error

    errorHandling chooses what happens next. The default is unchanged (throw the first error).

    aggregate waits until every source is attempted, then throws AggregateError with each parse/load failure:

    await loadDocuments('**/*.query.graphql', {
      loaders: [new GraphQLFileLoader()],
      errorHandling: 'aggregate',
    });

    return does not throw. Failed sources stay in the result as { location, error } without a document, next to the sources that parsed:

    const sources = await loadDocuments('**/*.query.graphql', {
      loaders: [new GraphQLFileLoader(), new CodeFileLoader()],
      errorHandling: 'return',
    });
    
    sources[0].document; // ValidQuery
    sources[1].document; // undefined
    sources[1].error; // GraphQLError: Syntax Error

    Source now has an optional error field. @graphql-tools/graphql-file-loader and @graphql-tools/code-file-loader honor the same option. CodeFileLoader's existing noSilentErrors default is unchanged unless errorHandling is set.

    Custom loader failures and incomplete inline documents (query Foo {) follow the same option. errorHandling: 'throw' raises the first failure, not AggregateError.

Patch Changes

  • Updated dependencies [6da2904, 7770067, f956bfe]:
    • @graphql-tools/utils@13.2.0
    • @graphql-tools/import@8.0.7
    • @graphql-tools/load-files@8.0.7

@graphql-tools/utils@13.2.0

Minor Changes

  • #8606 6da2904 Thanks @ardatan! - loadDocuments / loadTypedefs now accept errorHandling so a single invalid file does not have to hide the rest of the load.

    A glob that mixes a valid operation with broken SDL used to throw the first Syntax Error and drop every other file:

    # valid.query.graphql
    query ValidQuery {
      a
    }
    # broken-a.query.graphql
    query {
      a
    await loadDocuments('**/*.query.graphql', {
      loaders: [new GraphQLFileLoader()],
    });
    // throws Syntax Error

    errorHandling chooses what happens next. The default is unchanged (throw the first error).

    aggregate waits until every source is attempted, then throws AggregateError with each parse/load failure:

    await loadDocuments('**/*.query.graphql', {
      loaders: [new GraphQLFileLoader()],
      errorHandling: 'aggregate',
    });

    return does not throw. Failed sources stay in the result as { location, error } without a document, next to the sources that parsed:

    const sources = await loadDocuments('**/*.query.graphql', {
      loaders: [new GraphQLFileLoader(), new CodeFileLoader()],
      errorHandling: 'return',
    });
    
    sources[0].document; // ValidQuery
    sources[1].document; // undefined
    sources[1].error; // GraphQLError: Syntax Error

    Source now has an optional error field. @graphql-tools/graphql-file-loader and @graphql-tools/code-file-loader honor the same option. CodeFileLoader's existing noSilentErrors default is unchanged unless errorHandling is set.

    Custom loader failures and incomplete inline documents (query Foo {) follow the same option. errorHandling: 'throw' raises the first failure, not AggregateError.

Patch Changes

  • #8588 7770067 Thanks @ardatan! - rewireTypes (used by mapSchema and addTypes) now recreates only the types that actually reference a replaced type.

    mapSchema already changes just the mapped types. rewireTypes then walked every type and built a new GraphQLObjectType / enum / scalar so field pointers stayed consistent. That is correct, and it is overkill when a pipeline maps one type on a large schema: unrelated types, unused enums, and unused scalars were cloned on every pass.

    The crawl now finds types whose field, argument, interface, or union members point at a different object than the type map (including wrapper types created during mapping), then rebuilds only that set. Specified directives stay as-is. Types that do not reach a replacement keep the same JavaScript object.

    createStub returns the graphql-js specified scalars for String / ID / Int / Float / Boolean instead of building an object or input with that name. Stitch typeFromAST uses createStub for every field type.

    const schema = buildSchema(/* GraphQL */ `
      scalar Unused
      enum Color { RED }
      type Query { foo: Foo }
      type Foo { id: ID }
    `)
    
    const mapped = mapSchema(schema, {
      [MapperKind.OBJECT_TYPE]: type =>
        type.name === 'Foo'
          ? new GraphQLObjectType({ ...type.toConfig(), description: 'mapped' })
          : undefined,
    })
    
    mapped.getType('Unused') === schema.getType('Unused')
    mapped.getType('Color') === schema.getType('Color')
  • #8608 f956bfe Thanks @ardatan! - transformInputValue now keeps falsy list items when GraphQL coerces a singleton into a list.

    A [String] / [Int] / [Boolean] field can receive "", 0, or false instead of a one-element array. The previous asArray helper treated those as empty and produced [], so rename and other input transforms dropped a valid value. null is still returned as-is. A real list is still walked item by item.

@graphql-tools/webpack-loader@8.1.0

Minor Changes

  • #8605 ea2603a Thanks @ardatan! - @graphql-tools/webpack-loader (and @graphql-tools/jest-transform, which calls it) now accepts more than one GraphQL operation in the same file and exports each named operation, matching graphql-tag/loader.

    A file with two mutations used to throw GraphQL Webpack Loader allows only for one GraphQL Operation per file, so this import failed in webpack and Jest:

    # menuMutations.graphql
    mutation deleteMenuMutation {
      deleteMenu {
        ...MenuFields
      }
    }
    
    mutation upsertMenuMutation {
      upsertMenu {
        id
      }
    }
    
    fragment MenuFields on Menu {
      id
      name
    }
    import { deleteMenuMutation, upsertMenuMutation } from './menuMutations.graphql'

    The default export is still the whole document. Each named export is that operation plus the fragments it uses. deleteMenuMutation includes MenuFields; upsertMenuMutation does not.

    import menuMutations, {
      deleteMenuMutation,
      upsertMenuMutation,
    } from './menuMutations.graphql'

    A single named operation is also exported under its name. Unnamed operations are still allowed when the file has only one operation. If the file has more than one operation, each operation must be named.

    jest-transform now passes the Jest source path and filesystem into the loader, so esModule: true can emit named exports for operations and fragments pulled in with #import. CommonJS named exports use an own property so a GraphQL name of __proto__ does not replace the export object's prototype.

@graphql-tools/executor@4.1.6

Patch Changes

  • #8610 7832344 Thanks @ardatan! - Use compact null-prototype maps for per-field execution results.

    executeFields and promiseForObject now allocate result objects with a shared empty constructor then Object.setPrototypeOf(..., null), instead of Object.create(null). Dense responses create many of these maps; the named constructor gives V8 a reusable shape while keeping the same null-prototype semantics.

  • Updated dependencies [6da2904, 7770067, f956bfe]:

    • @graphql-tools/utils@13.2.0

@graphql-tools/executor-apollo-link@3.0.7

Patch Changes

@graphql-tools/executor-envelop@5.0.8

Patch Changes

@graphql-tools/executor-legacy-ws@2.0.7

Patch Changes

@graphql-tools/executor-urql-exchange@2.0.7

Patch Changes

@graphql-tools/executor-yoga@5.0.1

Patch Changes

  • Updated dependencies [6da2904, 7770067, f956bfe]:
    • @graphql-tools/utils@13.2.0
    • @graphql-tools/executor-envelop@5.0.8

@graphql-tools/graphql-tag-pluck@9.0.8

Patch Changes

graphql-tools@10.0.7

Patch Changes

  • Updated dependencies [902d870]:
    • @graphql-tools/schema@11.0.7

@graphql-tools/import@8.0.7

Patch Changes

@graphql-tools/links@11.0.7

Patch Changes

@graphql-tools/load-files@8.0.7

Patch Changes

@graphql-tools/apollo-engine-loader@9.0.7

Patch Changes

  • #8598 38e7800 Thanks @ardatan! - ApolloEngineLoader now sends operationName: "GetSchemaByTag" in the GraphQL request body.

    The query was already named GetSchemaByTag, but the POST body only had query and variables. Servers and Apollo Server plugins that require an operation name on the request (and read it from the JSON body, not the document) rejected this introspection call. The GitHub loader already sends operationName the same way.

  • Updated dependencies [6da2904, 7770067, f956bfe]:

    • @graphql-tools/utils@13.2.0

@graphql-tools/external-fragment-loader@1.0.8

Patch Changes

  • Updated dependencies [6da2904, 7770067, f956bfe]:
    • @graphql-tools/utils@13.2.0
    • @graphql-tools/graphql-tag-pluck@9.0.8
    • @graphql-tools/load-files@8.0.7

@graphql-tools/git-loader@9.0.8

Patch Changes

  • Updated dependencies [6da2904, 7770067, f956bfe]:
    • @graphql-tools/utils@13.2.0
    • @graphql-tools/graphql-tag-pluck@9.0.8

@graphql-tools/github-loader@10.0.8

Patch Changes

  • Updated dependencies [6da2904, 7770067, f956bfe]:
    • @graphql-tools/utils@13.2.0
    • @graphql-tools/graphql-tag-pluck@9.0.8

@graphql-tools/json-file-loader@9.0.7

Patch Changes

  • Updated dependencies [6da2904, 7770067, f956bfe]:
    • @graphql-tools/utils@13.2.0
    • @graphql-tools/load-files@8.0.7

@graphql-tools/module-loader@9.0.7

Patch Changes

@graphql-tools/url-loader@10.0.7

Patch Changes

  • Updated dependencies [6da2904, 7770067, f956bfe]:
    • @graphql-tools/utils@13.2.0
    • @graphql-tools/executor-legacy-ws@2.0.7

@graphql-tools/merge@10.0.7

Patch Changes

@graphql-tools/mock@10.1.2

Patch Changes

@graphql-tools/node-require@8.0.7

Patch Changes

@graphql-tools/relay-operation-optimizer@8.0.7

Patch Changes

@graphql-tools/resolvers-composition@8.0.7

Patch Changes

@graphql-tools/schema@11.0.7

Patch Changes

  • #8601 902d870 Thanks @ardatan! - mergeSchemas and makeExecutableSchema now accept onFieldTypeConflict, the same callback mergeTypeDefs already had.

    When two schemas define the same field with different types, default merge throws. When only nullability differs, it keeps the earlier type.

    # first schema
    type Post {
      id: ID!
      title: String!
      content: String!
      published: Boolean!
    }
    
    # second schema
    type Post {
      id: ID!
      title: String
      content: Int
      fieldA: Int
      fieldB: String
    }

    content: String! vs content: Int throws. title: String! vs title: String keeps String!.

    mergeSchemas({
      schemas: [firstSchema, secondSchema],
      onFieldTypeConflict: (_existing, incoming) => incoming,
    });

    The merged Post takes title: String and content: Int from the second schema, keeps published from the first, and adds fieldA / fieldB. Types that exist on only one schema, such as User on the first, stay.

    The same callback applies to field arguments. Returning the incoming field uses the second schema's argument types and still keeps arguments that exist on only one side. Returning the existing field with reverseArguments: true keeps the first schema's argument types and still appends new arguments.

  • Updated dependencies [6da2904, 7770067, f956bfe]:

    • @graphql-tools/utils@13.2.0
    • @graphql-tools/merge@10.0.7

Don't miss a new graphql-tools release

NewReleases is sending notifications on new releases.