Protip: Generate frontend TypeScript-types from GraphQL Schema

Hi guys,

I have a protip for you if you are using lib-guillotine or lib-graphql and TypeScript frontend! :grinning:

You can automaticallyl generate the TypeScript return type of your GraphQL-query by using graphql-code-generator.

Example:

I have my query in a file contentByPath.ts:

// language=GraphQL

export const contentByPath = /* GraphQL*/ `query ContentByPath ($path: ID) {
  guillotine {
    get(key: $path) {
        displayName
        ... on com_nerdforge_Article {
          data {
            intro {
              processedHtml
            }
          }
        }
    }
  }
}`

Running the npm cli command graphql-codegen will generate a file with the following type:

export type ContentByPathQuery = {
  __typename?: 'Query'
  guillotine?:
    | {
        __typename?: 'HeadlessCms'
        get?:
          | { __typename?: 'base_Folder'; displayName?: string | null | undefined }
          | { __typename?: 'base_Media'; displayName?: string | null | undefined }
          | { __typename?: 'base_Shortcut'; displayName?: string | null | undefined }
          | { __typename?: 'base_Structured'; displayName?: string | null | undefined }
          | { __typename?: 'base_Unstructured'; displayName?: string | null | undefined }
          | { __typename?: 'media_Archive'; displayName?: string | null | undefined }
          | { __typename?: 'media_Audio'; displayName?: string | null | undefined }
          | { __typename?: 'media_Code'; displayName?: string | null | undefined }
          | { __typename?: 'media_Data'; displayName?: string | null | undefined }
          | { __typename?: 'media_Document'; displayName?: string | null | undefined }
          | { __typename?: 'media_Executable'; displayName?: string | null | undefined }
          | { __typename?: 'media_Image'; displayName?: string | null | undefined }
          | { __typename?: 'media_Presentation'; displayName?: string | null | undefined }
          | { __typename?: 'media_Spreadsheet'; displayName?: string | null | undefined }
          | { __typename?: 'media_Text'; displayName?: string | null | undefined }
          | { __typename?: 'media_Unknown'; displayName?: string | null | undefined }
          | { __typename?: 'media_Vector'; displayName?: string | null | undefined }
          | { __typename?: 'media_Video'; displayName?: string | null | undefined }
          | {
              __typename?: 'com_nerdforge_Article'
              displayName?: string | null | undefined
              data?:
                | {
                    __typename?: 'com_nerdforge_Article_Data'
                    intro?:
                      | { __typename?: 'RichText'; processedHtml?: string | null | undefined }
                      | null
                      | undefined
                  }
                | null
                | undefined
            }
          | { __typename?: 'portal_Fragment'; displayName?: string | null | undefined }
          | { __typename?: 'portal_PageTemplate'; displayName?: string | null | undefined }
          | { __typename?: 'portal_Site'; displayName?: string | null | undefined }
          | { __typename?: 'portal_TemplateFolder'; displayName?: string | null | undefined }
          | null
          | undefined
      }
    | null
    | undefined
}

You can pick out specific โ€œsubtypesโ€ by using the buildt in Extract type in TypeScript.

type GraphQLContent = ContentByPathQuery['guillotine']['get'];
type GraphQLMediaImage = Extract<GraphQLContent, { 
    __typename: 'media_Image' 
  }>;
type GraphQLArticle = Extract<GraphQLContent, { 
    __typename: 'com_nerdforge_Article' 
  }>

Let me know in below if you want me to post my config files too, or if you are having any problems.

Good luck! :smiley:

1 Like