PR on GitHub: https://github.com/dotansimha/graphql-code-generator/pull/7383

Goals:

Challenges

Architecture

Internals

The base SDK code is static and the same for every project that uses the artifacts of the Typed Document Node Builder SDK.

The available fields and argument configuration is provided to the base SDK via TypeScript Generics.

For each of the entry root types (Query, Mutation, Subscription), three Generics must be provided.

  1. T_SelectionSet: Contains a tree of all fields that are selectable on a given root type (links to sub-ObjectTypes).

    // Example selection set tree structure
    
    type SDKSelectionSetUser = SDKSelectionSet<{
      id?: boolean;
      login?: boolean;
      bestFriend?: SDKSelectionSetUser;
    }>;
    // root query selection set
    type SDKSelectionSetQuery = SDKSelectionSet<{
      __typename?: boolean;
      user?: SDKSelectionSetUser & {
        [SDKFieldArgumentSymbol]: {
          id: string | never;
        };
      };
    }>;
    

    The SDKSelectionSet helper applies some GraphQL specific constraints.

    1. Require at least one field selection on a field that resolves to an ObjectType. An operation that does not at least select one field on an ObjectType fails the validation step.
    2. Only allow selecting fields that actually exist. TypeScript uses excess property checks. For a given type type A = { a: number } both const a1: A = { a: 1 } and const a2: A = { a: 1, b: 2 } are legal assignments. In our SDK we want minimal runtime overhead. The helper type allows treating the latter assignment as a compiler error. For more detail refer to the AtLeastOnePropertyOf and NoExtraProperties types that enforce this behavior.
  2. T_Arguments: Contains a tree of all optional and mandatory arguments on each given root type (links to sub-ObjectTypes). Instead of having this within T_SelectionSet this type is also used for doing advanced type matching for ensuring that field arguments only reference variable definitions with a matching type.

    // Example argument tree structure
    
    type SDKArgumentsUser = {
      friends:
        // Sub-arguments of the type this field resolves to. 
        SDKArgumentsUser
        // Arguments for this field.
        & {
          [SDKFieldArgumentSymbol]: {
            whereNameNeedle: 'String';
            first: 'Int',
            after: 'String'
          };
        }
    };
    
    type SDKArgumentsQuery = {
      user: SDKArgumentUser & {
        [SDKFieldArgumentSymbol]: {
          id: 'ID!';
        };
      };
    };
    

    The given example describes the argument required for the user field on the root Query type.

  3. T_Result: Contains a tree of the execution result (currently this is simply what the basic typescript codegen plugin emits).

In addition, a T_InputTypeMap type must be provided which is a lookup map for the variable values.

type SDKInputTypeMap = {
  String: string;
  Int: number;
  Boolean: number;
};

The instantiation of the SDK with those generics is the following: