0

So I just migrated my entire codebase for a project from using the direct Headless Wordpress GraphQL endpoint to using OneGraph for Google+Facebook Business support. OneGraph is an amazing tool, intend to use it in a tech ed course I'm writing on headless WordPress actually. Anyway, I am getting this apollo error, it is referencing the output codegen. Here's the error:

  graphQLErrors: [
    {
      message: 'Variable "$idTypeFoot" got invalid value 2; Expected type MenuNodeIdTypeEnum.',
      path: [Array],
      extensions: [Object]
    },
    {
      message: 'Variable "$idTypeFoot" got invalid value 2; Expected type MenuNodeIdTypeEnum.',
      path: [Array],
      extensions: [Object]
    }
  ],

Here's the output codegen definition in my generated/graphql.tsx file:


export enum WordpressMenuNodeIdTypeEnum {
    /** Identify a menu node by the Database ID. */
    DATABASE_ID = 0,
    /** Identify a menu node by the (hashed) Global ID. */
    ID = 1,
    /** Identify a menu node by it's name */
    NAME = 2
}

Here is the previous definition of that same enum prior to migrating my codebase to onegraph:


/** The Type of Identifier used to fetch a single node. Default is "ID". To be used along with the "id" field. */
export enum MenuNodeIdTypeEnum {
    /** Identify a menu node by the Database ID. */
    DatabaseId = 'DATABASE_ID',
    /** Identify a menu node by the (hashed) Global ID. */
    Id = 'ID',
    /** Identify a menu node by it's name */
    Name = 'NAME'
}

Here is the dynamic-nav-fields.graphql partial used in the parent query:

fragment DynamicNavFragment on WordpressMenuItem {
    id
    label
    path
    parentId
}

Here is the dynamic-nav.graphql parent query:

# import DynamicNavFragment from './Partials/dynamic-nav-fields.graphql'

query DynamicNav(
  $idHead: ID!
  $idTypeHead: WordpressMenuNodeIdTypeEnum!
  $idFoot: ID!
  $idTypeFoot: WordpressMenuNodeIdTypeEnum!
    ) {
    Header: wordpress {
        menu(id: $idHead, idType: $idTypeHead) {
            menuItems(where: { parentId: 0 }) {
                edges {
                    node {
                        ...DynamicNavFragment
                        childItems {
                            edges {
                                node {
                                    ...DynamicNavFragment
                                    childItems {
                                        edges {
                                            node {
                                                ...DynamicNavFragment
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
    Footer: wordpress {
        menu(id: $idFoot, idType: idTypeFoot) {
            menuItems(where: { parentId: 0 }) {
                edges {
                    node {
                        ...DynamicNavFragment
                        childItems {
                            edges {
                                node {
                                    ...DynamicNavFragment
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}

Here is my codegen.yml configuration file:

overwrite: true
schema:
  ${WORDPRESS_API_URL_YML}:
    headers:
      Authorization: Bearer ${WORDPRESS_AUTH_REFRESH_TOKEN_YML}
documents: 'graphql/**/*.graphql'
generates:
  graphql/generated/graphql.tsx:
    plugins:
      - typescript:
          constEnums: false
          enumsAsTypes: false          
          numericEnums: true
          futureProofEnums: false
          enumsAsConst: false
          onlyOperationTypes: false
          maybeValue: T | null | undefined
          noExport: false
          enumPrefix: true
          fieldWrapperValue: T
          wrapFieldDefinitions: true
          skipTypename: false
          nonOptionalTypename: false
          useTypeImports: false
          avoidOptionals: true
          declarationKind: 
            input: interface
            type: interface
      - typescript-operations:
          declarationKind:
            input: interface
            type: interface
          avoidOptionals: true
          exportFragmentSpreadSubTypes: true
      - typescript-react-apollo:
          addDocBlocks: true
          reactApolloVersion: 3
          documentMode: documentNodeImportFragments
    config:
      maybeValue: T | null | undefined
      declarationKind:
        input: interface
        type: interface
      documentNodeImportFragments: true
      reactApolloVersion: 3
      withHooks: true
      withHOC: false
      avoidOptionals: true
      withComponent: false
      exportFragmentSpreadSubTypes: true
      addDocBlocks: true
  graphql/graphql.schema.graphql:
    plugins:
      - schema-ast
    config:
      commentDescriptions: true
  graphql/graphql.schema.json:
    plugins:
      - introspection
    config:
      commentDescriptions: true
      
hooks:
  afterAllFileWrite: 
    - prettier --write

Why is OneGraph replacing the generated Enum values with numbers that apollo client can't read? If anyone knows how to resolve this please let me know

8
  • 1
    numericEnums: true ? Commented Apr 3, 2021 at 11:16
  • it was throwing an error prior to that because onegraph was outputing numeric enums regardless of the flag. I had it set to false initially and the only difference was wrapping 2 as "2" Commented Apr 3, 2021 at 11:17
  • just use WP types generated separately? graphql-code-generator.com ... you can customize enums graphql-code-generator.com/docs/plugins/… Commented Apr 3, 2021 at 13:30
  • 1
    a few steps [with manual work] later ... you can setup own federated API ;) Commented Apr 3, 2021 at 17:13
  • 1
    Is the schema available to test somewhere? I'd love to look at where the numeric enums are coming from! Commented Apr 3, 2021 at 20:20

2 Answers 2

1

tldr; change codegen.yml to use numericEnums: false:

generates:
  graphql/generated/graphql.tsx:
    plugins:
      - typescript:
          numericEnums: false

To track this down, I took the GraphQL SDL definition of WordpressMenuNodeIdTypeEnum:

enum WordpressMenuNodeIdTypeEnum {
    # Identify a menu node by the Database ID.
    DATABASE_ID
    # Identify a menu node by the (hashed) Global ID.
    ID
    # Identify a menu node by it's name
    NAME
}

along with the previous numericEnums: true setting in codegen.yml, and used them on the playground at https://www.graphql-code-generator.com numericEnums: true

As you can see in the screenshot, the SDL definition was compiled into numeric enums (the standard for typescript enums at runtime)

I then tested with numericEnums: false: numericEnums: false

And that output looks like what you'd expect, where WordpressMenuNodeIdTypeEnum.Name is equal to "NAME", so that Apollo gets the expected value instead of the runtime typescript-enum value (2).

Sign up to request clarification or add additional context in comments.

Comments

0

UPDATE — see @sgrove’s answer, he was awesome and helped me troubleshoot the issue!

okay, there were two options I had: (1) overwrite each enum converted from a string value representative of the __type previously or (2) hardcode these values in. To get the custom enum value I had to add the following to my codegen.yml:

config:
    enumValues:
       WordpressMenuNodeIdTypeEnum: '@/types/global-enums#WordpressMenuNodeIdTypeEnum'

which directs the generated code to import this enum definition from @/types/global-enums.ts

/** The Type of Identifier used to fetch a single node. Default is "ID". To be used along with the "id" field. */
export enum WordpressMenuNodeIdTypeEnum {
    /** Identify a menu node by the Database ID. */
    DatabaseId = 'DATABASE_ID',
    /** Identify a menu node by the (hashed) Global ID. */
    Id = 'ID',
    /** Identify a menu node by it's name */
    Name = 'NAME'
}

However, I then ran into another issue, and decided to hardcode the NAME value in for WordpressMenuNodeIdTypeEnum in my graphql schema directly:

# import DynamicNavFragment from './Partials/dynamic-nav-fields.graphql'

query DynamicNav($idHead: ID!, $idFoot: ID!) {
    Header: wordpress {
        menu(id: $idHead, idType: NAME) {
            menuItems(where: { parentId: 0 }) {
                edges {
                    node {
                        ...DynamicNavFragment
                        childItems {
                            edges {
                                node {
                                    ...DynamicNavFragment
                                    childItems {
                                        edges {
                                            node {
                                                ...DynamicNavFragment
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
    Footer: wordpress {
        menu(id: $idFoot, idType: NAME) {
            menuItems(where: { parentId: 0 }) {
                edges {
                    node {
                        ...DynamicNavFragment
                        childItems {
                            edges {
                                node {
                                    ...DynamicNavFragment
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}

Then I ran into an additional issue with the apollo cache since both a top-level ServicesTopQuery and DynamicNavQuery have a shared top level wordpress field with a corresponding __typename "WordPressRootQuery".

Codegen definition for ServicesTopQuery and ServicesTopQueryVariables:

export type ServicesTopQueryVariables = Exact<{
    popular: Scalars['String'];
    other: Scalars['String'];
}>;

export type ServicesTopQuery = { __typename?: 'Query' } & {
    popular: Maybe<
        { __typename?: 'WordpressRootQuery' } & {
            services: Maybe<
                { __typename?: 'WordpressRootQueryToServiceConnection' } & {
                    edges: Maybe<
                        Array<
                            Maybe<
                                {
                                    __typename?: 'WordpressRootQueryToServiceConnectionEdge';
                                } & {
                                    node: Maybe<
                                        {
                                            __typename?: 'WordpressService';
                                        } & ServiceFieldsTopFragment
                                    >;
                                }
                            >
                        >
                    >;
                }
            >;
        }
    >;
    other: Maybe<
        { __typename?: 'WordpressRootQuery' } & {
            services: Maybe<
                { __typename?: 'WordpressRootQueryToServiceConnection' } & {
                    edges: Maybe<
                        Array<
                            Maybe<
                                {
                                    __typename?: 'WordpressRootQueryToServiceConnectionEdge';
                                } & {
                                    node: Maybe<
                                        {
                                            __typename?: 'WordpressService';
                                        } & ServiceFieldsTopFragment
                                    >;
                                }
                            >
                        >
                    >;
                }
            >;
        }
    >;
};

Codegen definition for DynamicNavQuery and DynamicNavQueryVariables:


export type DynamicNavQueryVariables = Exact<{
    idHead: Scalars['ID'];
    idFoot: Scalars['ID'];
}>;

export type DynamicNavQuery = { __typename?: 'Query' } & {
    Header: Maybe<
        { __typename?: 'WordpressRootQuery' } & {
            menu: Maybe<
                { __typename?: 'WordpressMenu' } & {
                    menuItems: Maybe<
                        { __typename?: 'WordpressMenuToMenuItemConnection' } & {
                            edges: Maybe<
                                Array<
                                    Maybe<
                                        {
                                            __typename?: 'WordpressMenuToMenuItemConnectionEdge';
                                        } & {
                                            node: Maybe<
                                                { __typename?: 'WordpressMenuItem' } & {
                                                    childItems: Maybe<
                                                        {
                                                            __typename?: 'WordpressMenuItemToMenuItemConnection';
                                                        } & {
                                                            edges: Maybe<
                                                                Array<
                                                                    Maybe<
                                                                        {
                                                                            __typename?: 'WordpressMenuItemToMenuItemConnectionEdge';
                                                                        } & {
                                                                            node: Maybe<
                                                                                { __typename?: 'WordpressMenuItem' } & {
                                                                                    childItems: Maybe<
                                                                                        {
                                                                                            __typename?: 'WordpressMenuItemToMenuItemConnection';
                                                                                        } & {
                                                                                            edges: Maybe<
                                                                                                Array<
                                                                                                    Maybe<
                                                                                                        {
                                                                                                            __typename?: 'WordpressMenuItemToMenuItemConnectionEdge';
                                                                                                        } & {
                                                                                                            node: Maybe<
                                                                                                                {
                                                                                                                    __typename?: 'WordpressMenuItem';
                                                                                                                } & DynamicNavFragmentFragment
                                                                                                            >;
                                                                                                        }
                                                                                                    >
                                                                                                >
                                                                                            >;
                                                                                        }
                                                                                    >;
                                                                                } & DynamicNavFragmentFragment
                                                                            >;
                                                                        }
                                                                    >
                                                                >
                                                            >;
                                                        }
                                                    >;
                                                } & DynamicNavFragmentFragment
                                            >;
                                        }
                                    >
                                >
                            >;
                        }
                    >;
                }
            >;
        }
    >;
    Footer: Maybe<
        { __typename?: 'WordpressRootQuery' } & {
            menu: Maybe<
                { __typename?: 'WordpressMenu' } & {
                    menuItems: Maybe<
                        { __typename?: 'WordpressMenuToMenuItemConnection' } & {
                            edges: Maybe<
                                Array<
                                    Maybe<
                                        {
                                            __typename?: 'WordpressMenuToMenuItemConnectionEdge';
                                        } & {
                                            node: Maybe<
                                                { __typename?: 'WordpressMenuItem' } & {
                                                    childItems: Maybe<
                                                        {
                                                            __typename?: 'WordpressMenuItemToMenuItemConnection';
                                                        } & {
                                                            edges: Maybe<
                                                                Array<
                                                                    Maybe<
                                                                        {
                                                                            __typename?: 'WordpressMenuItemToMenuItemConnectionEdge';
                                                                        } & {
                                                                            node: Maybe<
                                                                                {
                                                                                    __typename?: 'WordpressMenuItem';
                                                                                } & DynamicNavFragmentFragment
                                                                            >;
                                                                        }
                                                                    >
                                                                >
                                                            >;
                                                        }
                                                    >;
                                                } & DynamicNavFragmentFragment
                                            >;
                                        }
                                    >
                                >
                            >;
                        }
                    >;
                }
            >;
        }
    >;
};

Code from pages/index.tsx where DynamicNavQuery data was initially populated then swiftly overwritten by ServicesTopQuery data

import { Container } from '@/components/UI';
import { initializeApollo, addApolloState } from '@/lib/apollo';
import { LandingHero } from '@/components/Landing';
import ServiceTopCoalesced from '@/components/ServicesTop/services-top-coalesced';
import AppLayout from '@/components/Layout/layout';
import {
    GetStaticPropsContext,
    GetStaticPropsResult,
    InferGetStaticPropsType
} from 'next';
import {
    ServicesTopQuery,
    ServicesTopQueryVariables,
    ServicesTopDocument,
    DynamicNavDocument,
    DynamicNavQueryVariables,
    DynamicNavQuery
} from '@/graphql/generated/graphql';

export function Index({
    other,
    popular,
    Header,
    Footer
}: InferGetStaticPropsType<typeof getStaticProps>) {
    return (
        <>
            <AppLayout
                title={'✂ The Fade Room Inc. ✂'}
                Header={Header}
                Footer={Footer}
                hero={<LandingHero />}
            >
                <Container clean className='fit'>
                    <ServiceTopCoalesced other={other} popular={popular} />
                </Container>
            </AppLayout>
        </>
    );
}

export async function getStaticProps(
    ctx: GetStaticPropsContext
): Promise<
    GetStaticPropsResult<{
        other: ServicesTopQuery['other'];
        popular: ServicesTopQuery['popular'];
        Header: DynamicNavQuery['Header'];
        Footer: DynamicNavQuery['Footer'];
    }>
> {
    const apolloClient = initializeApollo();
    await apolloClient.query<
        DynamicNavQuery,
        DynamicNavQueryVariables
    >({
        query: DynamicNavDocument,
        variables: {
            idHead: 'Header',
            idFoot: 'Footer'
        }
    });

    await apolloClient.query<
        ServicesTopQuery,
        ServicesTopQueryVariables
    >({
        query: ServicesTopDocument,
        variables: {
            other: 'all',
            popular: 'popular-service'
        }
    });
    return addApolloState(apolloClient, {
        props: {},
        revalidate: 10
    });
}
export default Index;

The DynamicNavQuery data was populated as intended, then my fallback loading component was rendered following initial DOM render for a quarter second or so, then the DynamicNavQuery data disappeared and ServicesTopQuery data was populated instead. So, the existing cache data was overwritten with incoming data sharing the same top level field wordpress and __typename WordPressRootQuery in the apollo client cache. To remedy this behavior I consulted the apollo docs and updated the instantiation of the new InMemoryCache class with the following:

cache: new InMemoryCache({
            typePolicies: {
                Query: {
                    fields: {
                        wordpress: {
                            merge(existing, incoming, { mergeObjects }) {
                                // invoking nested merge functions
                                return mergeObjects(existing, incoming);
                            }
                        }
                    }
                }
            }
        })
    });

The above prevented existing wordpress field data from being overwritten by incoming wordpress field data, resolving the issue. The process of migrating from a headless WordPress endpoint to a OneGraph endpoint proved more cumbersome than initially anticipated, so I figured I'd follow-up with a detailed response in case someone runs into a similar problem down the road.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.