Skip to content

May 24, 2023

Compare
Choose a tag to compare
@theguild-bot theguild-bot released this 24 May 08:16
· 328 commits to master since this release
5c7b3b3

@graphql-codegen/cli@4.0.0

Major Changes

  • bb66c2a31 Thanks @n1ru4l! - Require Node.js >= 16. Drop support for Node.js 14

Patch Changes

@graphql-codegen/core@4.0.0

Major Changes

  • bb66c2a31 Thanks @n1ru4l! - Require Node.js >= 16. Drop support for Node.js 14

Patch Changes

@graphql-codegen/add@5.0.0

Major Changes

  • bb66c2a31 Thanks @n1ru4l! - Require Node.js >= 16. Drop support for Node.js 14

Patch Changes

@graphql-codegen/fragment-matcher@5.0.0

Major Changes

  • bb66c2a31 Thanks @n1ru4l! - Require Node.js >= 16. Drop support for Node.js 14

Patch Changes

@graphql-codegen/introspection@4.0.0

Major Changes

  • bb66c2a31 Thanks @n1ru4l! - Require Node.js >= 16. Drop support for Node.js 14

Patch Changes

@graphql-codegen/schema-ast@4.0.0

Major Changes

  • bb66c2a31 Thanks @n1ru4l! - Require Node.js >= 16. Drop support for Node.js 14

Patch Changes

@graphql-codegen/time@5.0.0

Major Changes

  • bb66c2a31 Thanks @n1ru4l! - Require Node.js >= 16. Drop support for Node.js 14

Patch Changes

@graphql-codegen/visitor-plugin-common@4.0.0

Major Changes

  • #9375 ba84a3a27 Thanks @eddeee888! - Implement Scalars with input/output types

    In GraphQL, Scalar types can be different for client and server. For example, given the native GraphQL ID:

    • A client may send string or number in the input
    • A client receives string in its selection set (i.e output)
    • A server receives string in the resolver (GraphQL parses string or number received from the client to string)
    • A server may return string or number (GraphQL serializes the value to string before sending it to the client )

    Currently, we represent every Scalar with only one type. This is what codegen generates as base type:

    export type Scalars = {
      ID: string;
    };

    Then, this is used in both input and output type e.g.

    export type Book = {
      __typename?: 'Book';
      id: Scalars['ID']; // Output's ID can be `string` 👍
    };
    
    export type QueryBookArgs = {
      id: Scalars['ID']; // Input's ID can be `string` or `number`. However, the type is only `string` here 👎
    };

    This PR extends each Scalar to have input and output:

    export type Scalars = {
      ID: {
        input: string | number;
        output: string;
      };
    };

    Then, each input/output GraphQL type can correctly refer to the correct input/output scalar type:

    export type Book = {
      __typename?: 'Book';
      id: Scalars['ID']['output']; // Output's ID can be `string` 👍
    };
    
    export type QueryBookArgs = {
      id: Scalars['ID']['input']; // Input's ID can be `string` or `number` 👍
    };

    Note that for typescript-resolvers, the type of ID needs to be inverted. However, the referenced types in GraphQL input/output types should still work correctly:

    export type Scalars = {
      ID: {
        input: string;
        output: string | number;
      }
    }
    
    export type Book = {
      __typename?: "Book";
      id: Scalars["ID"]['output']; // Resolvers can return `string` or `number` in ID fields 👍
    };
    
    export type QueryBookArgs = {
      id: Scalars["ID"]['input']; // Resolvers receive `string` in ID fields 👍
    };
    
    export type ResolversTypes = {
      ID: ID: ResolverTypeWrapper<Scalars['ID']['output']>; // Resolvers can return `string` or `number` in ID fields 👍
    }
    
    export type ResolversParentTypes = {
      ID: Scalars['ID']['output']; // Resolvers receive `string` or `number` from parents 👍
    };

    Config changes:

    1. Scalars option can now take input/output types:
    config: {
      scalars: {
        ID: {
          input: 'string',
          output: 'string | number'
        }
      }
    }
    1. If a string is given (instead of an object with input/output fields), it will be used as both input and output types:
    config: {
      scalars: {
        ID: 'string'; // This means `string` will be used for both ID's input and output types
      }
    }
    1. BREAKING CHANGE: External module Scalar types need to be an object with input/output fields
    config: {
      scalars: {
        ID: './path/to/scalar-module';
      }
    }

    If correctly, wired up, the following will be generated:

    // Previously, imported `ID` type can be a primitive type, now it must be an object with input/output fields
    import { ID } from './path/to/scalar-module';
    
    export type Scalars = {
      ID: { input: ID['input']; output: ID['output'] };
    };

    BREAKING CHANGE: This changes Scalar types which could be referenced in other plugins. If you are a plugin maintainer and reference Scalar, please update your plugin to use the correct input/output types.

  • bb66c2a31 Thanks @n1ru4l! - Require Node.js >= 16. Drop support for Node.js 14

Minor Changes

  • #9196 3848a2b73 Thanks @beerose! - Add @defer directive support

    When a query includes a deferred fragment field, the server will return a partial response with the non-deferred fields first, followed by the remaining fields once they have been resolved.

    Once start using the @defer directive in your queries, the generated code will automatically include support for the directive.

    // src/index.tsx
    import { graphql } from './gql';
    const OrdersFragment = graphql(`
      fragment OrdersFragment on User {
        orders {
          id
          total
        }
      }
    `);
    const GetUserQuery = graphql(`
      query GetUser($id: ID!) {
        user(id: $id) {
          id
          name
          ...OrdersFragment @defer
        }
      }
    `);

    The generated type for GetUserQuery will have information that the fragment is incremental, meaning it may not be available right away.

    // gql/graphql.ts
    export type GetUserQuery = { __typename?: 'Query'; id: string; name: string } & ({
      __typename?: 'Query';
    } & {
      ' $fragmentRefs'?: { OrdersFragment: Incremental<OrdersFragment> };
    });

    Apart from generating code that includes support for the @defer directive, the Codegen also exports a utility function called isFragmentReady. You can use it to conditionally render components based on whether the data for a deferred
    fragment is available:

    const OrdersList = (props: { data: FragmentType<typeof OrdersFragment> }) => {
      const data = useFragment(OrdersFragment, props.data);
      return (
        // render orders list
      )
    };
    
    function App() {
      const { data } = useQuery(GetUserQuery);
      return (
        {data && (
          <>
            {isFragmentReady(GetUserQuery, OrdersFragment, data)
    					&& <OrdersList data={data} />}
          </>
        )}
      );
    }
    export default App;
  • #9339 50471e651 Thanks @AaronMoat! - Add excludeTypes config to resolversNonOptionalTypename

    This disables the adding of __typename in resolver types for any specified typename. This could be useful e.g. if you're wanting to enable this for all new types going forward but not do a big migration.

    Usage example:

    const config: CodegenConfig = {
      schema: 'src/schema/**/*.graphql',
      generates: {
        'src/schema/types.ts': {
          plugins: ['typescript', 'typescript-resolvers'],
          config: {
            resolversNonOptionalTypename: {
              unionMember: true,
              excludeTypes: ['MyType'],
            },
          },
        },
      },
    };
  • #9229 5aa95aa96 Thanks @eddeee888! - Use generic to simplify ResolversUnionTypes

    This follows the ResolversInterfaceTypes's approach where the RefType generic is used to refer back to ResolversTypes or ResolversParentTypes in cases of nested Union types

  • #9304 e1dc75f3c Thanks @esfomeado! - Added support for disabling suffixes on Enums.

  • #9229 5aa95aa96 Thanks @eddeee888! - Extract interfaces to ResolversInterfaceTypes and add to resolversNonOptionalTypename

    1. ResolversInterfaceTypes is a new type that keeps track of a GraphQL interface and its implementing types.

    For example, consider this schema:

    extend type Query {
      character(id: ID!): CharacterNode
    }
    
    interface CharacterNode {
      id: ID!
    }
    
    type Wizard implements CharacterNode {
      id: ID!
      screenName: String!
      spells: [String!]!
    }
    
    type Fighter implements CharacterNode {
      id: ID!
      screenName: String!
      powerLevel: Int!
    }

    The generated types will look like this:

    export type ResolversInterfaceTypes<RefType extends Record<string, unknown>> = {
      CharacterNode: Fighter | Wizard;
    };
    
    export type ResolversTypes = {
      // other types...
      CharacterNode: ResolverTypeWrapper<ResolversInterfaceTypes<ResolversTypes>['CharacterNode']>;
      Fighter: ResolverTypeWrapper<Fighter>;
      Wizard: ResolverTypeWrapper<Wizard>;
      // other types...
    };
    
    export type ResolversParentTypes = {
      // other types...
      CharacterNode: ResolversInterfaceTypes<ResolversParentTypes>['CharacterNode'];
      Fighter: Fighter;
      Wizard: Wizard;
      // other types...
    };

    The RefType generic is used to reference back to ResolversTypes and ResolversParentTypes in some cases such as field returning a Union.

    1. resolversNonOptionalTypename also affects ResolversInterfaceTypes

    Using the schema above, if we use resolversNonOptionalTypename option:

    const config: CodegenConfig = {
      schema: 'src/schema/**/*.graphql',
      generates: {
        'src/schema/types.ts': {
          plugins: ['typescript', 'typescript-resolvers'],
          config: {
            resolversNonOptionalTypename: true, // Or `resolversNonOptionalTypename: { interfaceImplementingType: true }`
          },
        },
      },
    };

    Then, the generated type looks like this:

    export type ResolversInterfaceTypes<RefType extends Record<string, unknown>> = {
      CharacterNode: (Fighter & { __typename: 'Fighter' }) | (Wizard & { __typename: 'Wizard' });
    };
    
    export type ResolversTypes = {
      // other types...
      CharacterNode: ResolverTypeWrapper<ResolversInterfaceTypes<ResolversTypes>['CharacterNode']>;
      Fighter: ResolverTypeWrapper<Fighter>;
      Wizard: ResolverTypeWrapper<Wizard>;
      // other types...
    };
    
    export type ResolversParentTypes = {
      // other types...
      CharacterNode: ResolversInterfaceTypes<ResolversParentTypes>['CharacterNode'];
      Fighter: Fighter;
      Wizard: Wizard;
      // other types...
    };

Patch Changes

@graphql-codegen/typescript-document-nodes@4.0.0

Major Changes

  • bb66c2a31 Thanks @n1ru4l! - Require Node.js >= 16. Drop support for Node.js 14

Patch Changes

@graphql-codegen/gql-tag-operations@4.0.0

Major Changes

  • bb66c2a31 Thanks @n1ru4l! - Require Node.js >= 16. Drop support for Node.js 14

Patch Changes

@graphql-codegen/typescript-operations@4.0.0

Major Changes

  • #9375 ba84a3a27 Thanks @eddeee888! - Implement Scalars with input/output types

    In GraphQL, Scalar types can be different for client and server. For example, given the native GraphQL ID:

    • A client may send string or number in the input
    • A client receives string in its selection set (i.e output)
    • A server receives string in the resolver (GraphQL parses string or number received from the client to string)
    • A server may return string or number (GraphQL serializes the value to string before sending it to the client )

    Currently, we represent every Scalar with only one type. This is what codegen generates as base type:

    export type Scalars = {
      ID: string;
    };

    Then, this is used in both input and output type e.g.

    export type Book = {
      __typename?: 'Book';
      id: Scalars['ID']; // Output's ID can be `string` 👍
    };
    
    export type QueryBookArgs = {
      id: Scalars['ID']; // Input's ID can be `string` or `number`. However, the type is only `string` here 👎
    };

    This PR extends each Scalar to have input and output:

    export type Scalars = {
      ID: {
        input: string | number;
        output: string;
      };
    };

    Then, each input/output GraphQL type can correctly refer to the correct input/output scalar type:

    export type Book = {
      __typename?: 'Book';
      id: Scalars['ID']['output']; // Output's ID can be `string` 👍
    };
    
    export type QueryBookArgs = {
      id: Scalars['ID']['input']; // Input's ID can be `string` or `number` 👍
    };

    Note that for typescript-resolvers, the type of ID needs to be inverted. However, the referenced types in GraphQL input/output types should still work correctly:

    export type Scalars = {
      ID: {
        input: string;
        output: string | number;
      }
    }
    
    export type Book = {
      __typename?: "Book";
      id: Scalars["ID"]['output']; // Resolvers can return `string` or `number` in ID fields 👍
    };
    
    export type QueryBookArgs = {
      id: Scalars["ID"]['input']; // Resolvers receive `string` in ID fields 👍
    };
    
    export type ResolversTypes = {
      ID: ID: ResolverTypeWrapper<Scalars['ID']['output']>; // Resolvers can return `string` or `number` in ID fields 👍
    }
    
    export type ResolversParentTypes = {
      ID: Scalars['ID']['output']; // Resolvers receive `string` or `number` from parents 👍
    };

    Config changes:

    1. Scalars option can now take input/output types:
    config: {
      scalars: {
        ID: {
          input: 'string',
          output: 'string | number'
        }
      }
    }
    1. If a string is given (instead of an object with input/output fields), it will be used as both input and output types:
    config: {
      scalars: {
        ID: 'string'; // This means `string` will be used for both ID's input and output types
      }
    }
    1. BREAKING CHANGE: External module Scalar types need to be an object with input/output fields
    config: {
      scalars: {
        ID: './path/to/scalar-module';
      }
    }

    If correctly, wired up, the following will be generated:

    // Previously, imported `ID` type can be a primitive type, now it must be an object with input/output fields
    import { ID } from './path/to/scalar-module';
    
    export type Scalars = {
      ID: { input: ID['input']; output: ID['output'] };
    };

    BREAKING CHANGE: This changes Scalar types which could be referenced in other plugins. If you are a plugin maintainer and reference Scalar, please update your plugin to use the correct input/output types.

  • bb66c2a31 Thanks @n1ru4l! - Require Node.js >= 16. Drop support for Node.js 14

Minor Changes

  • #9196 3848a2b73 Thanks @beerose! - Add @defer directive support

    When a query includes a deferred fragment field, the server will return a partial response with the non-deferred fields first, followed by the remaining fields once they have been resolved.

    Once start using the @defer directive in your queries, the generated code will automatically include support for the directive.

    // src/index.tsx
    import { graphql } from './gql';
    const OrdersFragment = graphql(`
      fragment OrdersFragment on User {
        orders {
          id
          total
        }
      }
    `);
    const GetUserQuery = graphql(`
      query GetUser($id: ID!) {
        user(id: $id) {
          id
          name
          ...OrdersFragment @defer
        }
      }
    `);

    The generated type for GetUserQuery will have information that the fragment is incremental, meaning it may not be available right away.

    // gql/graphql.ts
    export type GetUserQuery = { __typename?: 'Query'; id: string; name: string } & ({
      __typename?: 'Query';
    } & {
      ' $fragmentRefs'?: { OrdersFragment: Incremental<OrdersFragment> };
    });

    Apart from generating code that includes support for the @defer directive, the Codegen also exports a utility function called isFragmentReady. You can use it to conditionally render components based on whether the data for a deferred
    fragment is available:

    const OrdersList = (props: { data: FragmentType<typeof OrdersFragment> }) => {
      const data = useFragment(OrdersFragment, props.data);
      return (
        // render orders list
      )
    };
    
    function App() {
      const { data } = useQuery(GetUserQuery);
      return (
        {data && (
          <>
            {isFragmentReady(GetUserQuery, OrdersFragment, data)
    					&& <OrdersList data={data} />}
          </>
        )}
      );
    }
    export default App;
  • #9304 e1dc75f3c Thanks @esfomeado! - Added support for disabling suffixes on Enums.

Patch Changes

@graphql-codegen/typescript-resolvers@4.0.0

Major Changes

  • #9375 ba84a3a27 Thanks @eddeee888! - Implement Scalars with input/output types

    In GraphQL, Scalar types can be different for client and server. For example, given the native GraphQL ID:

    • A client may send string or number in the input
    • A client receives string in its selection set (i.e output)
    • A server receives string in the resolver (GraphQL parses string or number received from the client to string)
    • A server may return string or number (GraphQL serializes the value to string before sending it to the client )

    Currently, we represent every Scalar with only one type. This is what codegen generates as base type:

    export type Scalars = {
      ID: string;
    };

    Then, this is used in both input and output type e.g.

    export type Book = {
      __typename?: 'Book';
      id: Scalars['ID']; // Output's ID can be `string` 👍
    };
    
    export type QueryBookArgs = {
      id: Scalars['ID']; // Input's ID can be `string` or `number`. However, the type is only `string` here 👎
    };

    This PR extends each Scalar to have input and output:

    export type Scalars = {
      ID: {
        input: string | number;
        output: string;
      };
    };

    Then, each input/output GraphQL type can correctly refer to the correct input/output scalar type:

    export type Book = {
      __typename?: 'Book';
      id: Scalars['ID']['output']; // Output's ID can be `string` 👍
    };
    
    export type QueryBookArgs = {
      id: Scalars['ID']['input']; // Input's ID can be `string` or `number` 👍
    };

    Note that for typescript-resolvers, the type of ID needs to be inverted. However, the referenced types in GraphQL input/output types should still work correctly:

    export type Scalars = {
      ID: {
        input: string;
        output: string | number;
      }
    }
    
    export type Book = {
      __typename?: "Book";
      id: Scalars["ID"]['output']; // Resolvers can return `string` or `number` in ID fields 👍
    };
    
    export type QueryBookArgs = {
      id: Scalars["ID"]['input']; // Resolvers receive `string` in ID fields 👍
    };
    
    export type ResolversTypes = {
      ID: ID: ResolverTypeWrapper<Scalars['ID']['output']>; // Resolvers can return `string` or `number` in ID fields 👍
    }
    
    export type ResolversParentTypes = {
      ID: Scalars['ID']['output']; // Resolvers receive `string` or `number` from parents 👍
    };

    Config changes:

    1. Scalars option can now take input/output types:
    config: {
      scalars: {
        ID: {
          input: 'string',
          output: 'string | number'
        }
      }
    }
    1. If a string is given (instead of an object with input/output fields), it will be used as both input and output types:
    config: {
      scalars: {
        ID: 'string'; // This means `string` will be used for both ID's input and output types
      }
    }
    1. BREAKING CHANGE: External module Scalar types need to be an object with input/output fields
    config: {
      scalars: {
        ID: './path/to/scalar-module';
      }
    }

    If correctly, wired up, the following will be generated:

    // Previously, imported `ID` type can be a primitive type, now it must be an object with input/output fields
    import { ID } from './path/to/scalar-module';
    
    export type Scalars = {
      ID: { input: ID['input']; output: ID['output'] };
    };

    BREAKING CHANGE: This changes Scalar types which could be referenced in other plugins. If you are a plugin maintainer and reference Scalar, please update your plugin to use the correct input/output types.

  • bb66c2a31 Thanks @n1ru4l! - Require Node.js >= 16. Drop support for Node.js 14

Minor Changes

  • #9196 3848a2b73 Thanks @beerose! - Add @defer directive support

    When a query includes a deferred fragment field, the server will return a partial response with the non-deferred fields first, followed by the remaining fields once they have been resolved.

    Once start using the @defer directive in your queries, the generated code will automatically include support for the directive.

    // src/index.tsx
    import { graphql } from './gql';
    const OrdersFragment = graphql(`
      fragment OrdersFragment on User {
        orders {
          id
          total
        }
      }
    `);
    const GetUserQuery = graphql(`
      query GetUser($id: ID!) {
        user(id: $id) {
          id
          name
          ...OrdersFragment @defer
        }
      }
    `);

    The generated type for GetUserQuery will have information that the fragment is incremental, meaning it may not be available right away.

    // gql/graphql.ts
    export type GetUserQuery = { __typename?: 'Query'; id: string; name: string } & ({
      __typename?: 'Query';
    } & {
      ' $fragmentRefs'?: { OrdersFragment: Incremental<OrdersFragment> };
    });

    Apart from generating code that includes support for the @defer directive, the Codegen also exports a utility function called isFragmentReady. You can use it to conditionally render components based on whether the data for a deferred
    fragment is available:

    const OrdersList = (props: { data: FragmentType<typeof OrdersFragment> }) => {
      const data = useFragment(OrdersFragment, props.data);
      return (
        // render orders list
      )
    };
    
    function App() {
      const { data } = useQuery(GetUserQuery);
      return (
        {data && (
          <>
            {isFragmentReady(GetUserQuery, OrdersFragment, data)
    					&& <OrdersList data={data} />}
          </>
        )}
      );
    }
    export default App;
  • #9339 50471e651 Thanks @AaronMoat! - Add excludeTypes config to resolversNonOptionalTypename

    This disables the adding of __typename in resolver types for any specified typename. This could be useful e.g. if you're wanting to enable this for all new types going forward but not do a big migration.

    Usage example:

    const config: CodegenConfig = {
      schema: 'src/schema/**/*.graphql',
      generates: {
        'src/schema/types.ts': {
          plugins: ['typescript', 'typescript-resolvers'],
          config: {
            resolversNonOptionalTypename: {
              unionMember: true,
              excludeTypes: ['MyType'],
            },
          },
        },
      },
    };
  • #9229 5aa95aa96 Thanks @eddeee888! - Use generic to simplify ResolversUnionTypes

    This follows the ResolversInterfaceTypes's approach where the RefType generic is used to refer back to ResolversTypes or ResolversParentTypes in cases of nested Union types

  • #9304 e1dc75f3c Thanks @esfomeado! - Added support for disabling suffixes on Enums.

  • #9229 5aa95aa96 Thanks @eddeee888! - Extract interfaces to ResolversInterfaceTypes and add to resolversNonOptionalTypename

    1. ResolversInterfaceTypes is a new type that keeps track of a GraphQL interface and its implementing types.

    For example, consider this schema:

    extend type Query {
      character(id: ID!): CharacterNode
    }
    
    interface CharacterNode {
      id: ID!
    }
    
    type Wizard implements CharacterNode {
      id: ID!
      screenName: String!
      spells: [String!]!
    }
    
    type Fighter implements CharacterNode {
      id: ID!
      screenName: String!
      powerLevel: Int!
    }

    The generated types will look like this:

    export type ResolversInterfaceTypes<RefType extends Record<string, unknown>> = {
      CharacterNode: Fighter | Wizard;
    };
    
    export type ResolversTypes = {
      // other types...
      CharacterNode: ResolverTypeWrapper<ResolversInterfaceTypes<ResolversTypes>['CharacterNode']>;
      Fighter: ResolverTypeWrapper<Fighter>;
      Wizard: ResolverTypeWrapper<Wizard>;
      // other types...
    };
    
    export type ResolversParentTypes = {
      // other types...
      CharacterNode: ResolversInterfaceTypes<ResolversParentTypes>['CharacterNode'];
      Fighter: Fighter;
      Wizard: Wizard;
      // other types...
    };

    The RefType generic is used to reference back to ResolversTypes and ResolversParentTypes in some cases such as field returning a Union.

    1. resolversNonOptionalTypename also affects ResolversInterfaceTypes

    Using the schema above, if we use resolversNonOptionalTypename option:

    const config: CodegenConfig = {
      schema: 'src/schema/**/*.graphql',
      generates: {
        'src/schema/types.ts': {
          plugins: ['typescript', 'typescript-resolvers'],
          config: {
            resolversNonOptionalTypename: true, // Or `resolversNonOptionalTypename: { interfaceImplementingType: true }`
          },
        },
      },
    };

    Then, the generated type looks like this:

    export type ResolversInterfaceTypes<RefType extends Record<string, unknown>> = {
      CharacterNode: (Fighter & { __typename: 'Fighter' }) | (Wizard & { __typename: 'Wizard' });
    };
    
    export type ResolversTypes = {
      // other types...
      CharacterNode: ResolverTypeWrapper<ResolversInterfaceTypes<ResolversTypes>['CharacterNode']>;
      Fighter: ResolverTypeWrapper<Fighter>;
      Wizard: ResolverTypeWrapper<Wizard>;
      // other types...
    };
    
    export type ResolversParentTypes = {
      // other types...
      CharacterNode: ResolversInterfaceTypes<ResolversParentTypes>['CharacterNode'];
      Fighter: Fighter;
      Wizard: Wizard;
      // other types...
    };

Patch Changes

@graphql-codegen/typed-document-node@5.0.0

Major Changes

  • bb66c2a31 Thanks @n1ru4l! - Require Node.js >= 16. Drop support for Node.js 14

Minor Changes

  • #9196 3848a2b73 Thanks @beerose! - Add @defer directive support

    When a query includes a deferred fragment field, the server will return a partial response with the non-deferred fields first, followed by the remaining fields once they have been resolved.

    Once start using the @defer directive in your queries, the generated code will automatically include support for the directive.

    // src/index.tsx
    import { graphql } from './gql';
    const OrdersFragment = graphql(`
      fragment OrdersFragment on User {
        orders {
          id
          total
        }
      }
    `);
    const GetUserQuery = graphql(`
      query GetUser($id: ID!) {
        user(id: $id) {
          id
          name
          ...OrdersFragment @defer
        }
      }
    `);

    The generated type for GetUserQuery will have information that the fragment is incremental, meaning it may not be available right away.

    // gql/graphql.ts
    export type GetUserQuery = { __typename?: 'Query'; id: string; name: string } & ({
      __typename?: 'Query';
    } & {
      ' $fragmentRefs'?: { OrdersFragment: Incremental<OrdersFragment> };
    });

    Apart from generating code that includes support for the @defer directive, the Codegen also exports a utility function called isFragmentReady. You can use it to conditionally render components based on whether the data for a deferred
    fragment is available:

    const OrdersList = (props: { data: FragmentType<typeof OrdersFragment> }) => {
      const data = useFragment(OrdersFragment, props.data);
      return (
        // render orders list
      )
    };
    
    function App() {
      const { data } = useQuery(GetUserQuery);
      return (
        {data && (
          <>
            {isFragmentReady(GetUserQuery, OrdersFragment, data)
    					&& <OrdersList data={data} />}
          </>
        )}
      );
    }
    export default App;

Patch Changes

@graphql-codegen/typescript@4.0.0

Major Changes

  • #9375 ba84a3a27 Thanks @eddeee888! - Implement Scalars with input/output types

    In GraphQL, Scalar types can be different for client and server. For example, given the native GraphQL ID:

    • A client may send string or number in the input
    • A client receives string in its selection set (i.e output)
    • A server receives string in the resolver (GraphQL parses string or number received from the client to string)
    • A server may return string or number (GraphQL serializes the value to string before sending it to the client )

    Currently, we represent every Scalar with only one type. This is what codegen generates as base type:

    export type Scalars = {
      ID: string;
    };

    Then, this is used in both input and output type e.g.

    export type Book = {
      __typename?: 'Book';
      id: Scalars['ID']; // Output's ID can be `string` 👍
    };
    
    export type QueryBookArgs = {
      id: Scalars['ID']; // Input's ID can be `string` or `number`. However, the type is only `string` here 👎
    };

    This PR extends each Scalar to have input and output:

    export type Scalars = {
      ID: {
        input: string | number;
        output: string;
      };
    };

    Then, each input/output GraphQL type can correctly refer to the correct input/output scalar type:

    export type Book = {
      __typename?: 'Book';
      id: Scalars['ID']['output']; // Output's ID can be `string` 👍
    };
    
    export type QueryBookArgs = {
      id: Scalars['ID']['input']; // Input's ID can be `string` or `number` 👍
    };

    Note that for typescript-resolvers, the type of ID needs to be inverted. However, the referenced types in GraphQL input/output types should still work correctly:

    export type Scalars = {
      ID: {
        input: string;
        output: string | number;
      }
    }
    
    export type Book = {
      __typename?: "Book";
      id: Scalars["ID"]['output']; // Resolvers can return `string` or `number` in ID fields 👍
    };
    
    export type QueryBookArgs = {
      id: Scalars["ID"]['input']; // Resolvers receive `string` in ID fields 👍
    };
    
    export type ResolversTypes = {
      ID: ID: ResolverTypeWrapper<Scalars['ID']['output']>; // Resolvers can return `string` or `number` in ID fields 👍
    }
    
    export type ResolversParentTypes = {
      ID: Scalars['ID']['output']; // Resolvers receive `string` or `number` from parents 👍
    };

    Config changes:

    1. Scalars option can now take input/output types:
    config: {
      scalars: {
        ID: {
          input: 'string',
          output: 'string | number'
        }
      }
    }
    1. If a string is given (instead of an object with input/output fields), it will be used as both input and output types:
    config: {
      scalars: {
        ID: 'string'; // This means `string` will be used for both ID's input and output types
      }
    }
    1. BREAKING CHANGE: External module Scalar types need to be an object with input/output fields
    config: {
      scalars: {
        ID: './path/to/scalar-module';
      }
    }

    If correctly, wired up, the following will be generated:

    // Previously, imported `ID` type can be a primitive type, now it must be an object with input/output fields
    import { ID } from './path/to/scalar-module';
    
    export type Scalars = {
      ID: { input: ID['input']; output: ID['output'] };
    };

    BREAKING CHANGE: This changes Scalar types which could be referenced in other plugins. If you are a plugin maintainer and reference Scalar, please update your plugin to use the correct input/output types.

  • bb66c2a31 Thanks @n1ru4l! - Require Node.js >= 16. Drop support for Node.js 14

Minor Changes

  • #9196 3848a2b73 Thanks @beerose! - Add @defer directive support

    When a query includes a deferred fragment field, the server will return a partial response with the non-deferred fields first, followed by the remaining fields once they have been resolved.

    Once start using the @defer directive in your queries, the generated code will automatically include support for the directive.

    // src/index.tsx
    import { graphql } from './gql';
    const OrdersFragment = graphql(`
      fragment OrdersFragment on User {
        orders {
          id
          total
        }
      }
    `);
    const GetUserQuery = graphql(`
      query GetUser($id: ID!) {
        user(id: $id) {
          id
          name
          ...OrdersFragment @defer
        }
      }
    `);

    The generated type for GetUserQuery will have information that the fragment is incremental, meaning it may not be available right away.

    // gql/graphql.ts
    export type GetUserQuery = { __typename?: 'Query'; id: string; name: string } & ({
      __typename?: 'Query';
    } & {
      ' $fragmentRefs'?: { OrdersFragment: Incremental<OrdersFragment> };
    });

    Apart from generating code that includes support for the @defer directive, the Codegen also exports a utility function called isFragmentReady. You can use it to conditionally render components based on whether the data for a deferred
    fragment is available:

    const OrdersList = (props: { data: FragmentType<typeof OrdersFragment> }) => {
      const data = useFragment(OrdersFragment, props.data);
      return (
        // render orders list
      )
    };
    
    function App() {
      const { data } = useQuery(GetUserQuery);
      return (
        {data && (
          <>
            {isFragmentReady(GetUserQuery, OrdersFragment, data)
    					&& <OrdersList data={data} />}
          </>
        )}
      );
    }
    export default App;
  • #9304 e1dc75f3c Thanks @esfomeado! - Added support for disabling suffixes on Enums.

Patch Changes

@graphql-codegen/client-preset@4.0.0

Major Changes

  • bb66c2a31 Thanks @n1ru4l! - Require Node.js >= 16. Drop support for Node.js 14

Minor Changes

  • #9196 3848a2b73 Thanks @beerose! - Add @defer directive support

    When a query includes a deferred fragment field, the server will return a partial response with the non-deferred fields first, followed by the remaining fields once they have been resolved.

    Once start using the @defer directive in your queries, the generated code will automatically include support for the directive.

    // src/index.tsx
    import { graphql } from './gql';
    const OrdersFragment = graphql(`
      fragment OrdersFragment on User {
        orders {
          id
          total
        }
      }
    `);
    const GetUserQuery = graphql(`
      query GetUser($id: ID!) {
        user(id: $id) {
          id
          name
          ...OrdersFragment @defer
        }
      }
    `);

    The generated type for GetUserQuery will have information that the fragment is incremental, meaning it may not be available right away.

    // gql/graphql.ts
    export type GetUserQuery = { __typename?: 'Query'; id: string; name: string } & ({
      __typename?: 'Query';
    } & {
      ' $fragmentRefs'?: { OrdersFragment: Incremental<OrdersFragment> };
    });

    Apart from generating code that includes support for the @defer directive, the Codegen also exports a utility function called isFragmentReady. You can use it to conditionally render components based on whether the data for a deferred
    fragment is available:

    const OrdersList = (props: { data: FragmentType<typeof OrdersFragment> }) => {
      const data = useFragment(OrdersFragment, props.data);
      return (
        // render orders list
      )
    };
    
    function App() {
      const { data } = useQuery(GetUserQuery);
      return (
        {data && (
          <>
            {isFragmentReady(GetUserQuery, OrdersFragment, data)
    					&& <OrdersList data={data} />}
          </>
        )}
      );
    }
    export default App;
  • #9353 d7e335b58 Thanks @charpeni! - Implement the ability the specify the hash algorithm used for persisted documents via persistedDocuments.hashAlgorithm

Patch Changes

@graphql-codegen/graphql-modules-preset@4.0.0

Major Changes

  • bb66c2a31 Thanks @n1ru4l! - Require Node.js >= 16. Drop support for Node.js 14

Patch Changes

@graphql-codegen/testing@3.0.0

Major Changes

  • bb66c2a31 Thanks @n1ru4l! - Require Node.js >= 16. Drop support for Node.js 14

Patch Changes

@graphql-codegen/plugin-helpers@5.0.0

Major Changes

  • bb66c2a31 Thanks @n1ru4l! - Require Node.js >= 16. Drop support for Node.js 14

Patch Changes