typescriptgraphqlgatsbycontentfulgatsby-source-contentful

Gatsby fails creating Contentful pages in TypeScript


I am trying to create Contentful dynamic pages in Gatsby + Typescript. Right now, I am able to fetch data while using GraphQL in a browser. The problem arises when I try to fetch data in gatsby-node.ts. It seems the pages are being generated but not completely. Also, I get this error:

ERROR #12100 API.TYPESCRIPT.TYPEGEN

There was an error while trying to generate TS types from your GraphQL queries:

Error: GraphQL Document Validation failed with 1 errors;   Error 0: This anonymous operation must be the only defined operation.
at /home/ugnius/GIT/New-webpage/gatsby-node.ts:1:1

See our docs page for more info on this error: https://gatsby.dev/graphql-typegen

failed Generating GraphQL and TypeScript types - 0.109s

gatsby-node.ts

import type { GatsbyNode } from 'gatsby';
import * as path from 'path';

export const createPages: GatsbyNode['createPages'] = async ({ graphql, actions }) => {
  const { createPage } = actions;

  const request = await graphql<Queries.Query>(`
    {
      allContentfulInsight {
        edges {
          node {
            title
            slug
          }
        }
      }
    }
  `);

  const insightTemplate = path.resolve('./src/templates/Insight.tsx');
  const insights = request?.data?.allContentfulInsight.edges;

  insights?.forEach((insight, index) => {
    createPage({
      path: `insights/${insight.node.slug}`,
      component: insightTemplate,
      context: {
        slug: insight.node.slug,
      },
    });
  });
};

src/templates/Insight.tsx

import * as React from 'react';
import { graphql } from 'gatsby';

const InsightTemplate: React.FC<Queries.InsightBySlugQuery> = (data) => {
  const title = data?.contentfulInsight?.title; // => undefined
  console.log(data); // prints correct data from Contentful but it is undefined in code

  return (
    <>
      <h1>{title}</h1>
    </>
  );
};

export default InsightTemplate;

export const query = graphql`
  query InsightBySlug($slug: String!) {
    site {
      siteMetadata {
        title
      }
    }
    contentfulInsight(slug: { eq: $slug }) {
      title
      content {
        raw
      }
    }
  }
`;

gatsby-types.dts

declare namespace Queries {
  // ...
  type Query = {
    readonly allContentfulInsight: ContentfulInsightConnection;
    // ...
  };
  // ...
  type ContentfulInsightConnection = {
    readonly edges: ReadonlyArray<ContentfulInsightEdge>;
    // ...
  };
  // ...
  type ContentfulInsightEdge = {
    readonly node: ContentfulInsight;
    // ...
  };
  // ...
  type ContentfulInsight = ContentfulEntry & ContentfulReference & Node & {
    readonly slug: Maybe<Scalars['String']>;
    readonly title: Maybe<Scalars['String']>;
    readonly content: Maybe<ContentfulInsightContent>;
    // ...
  };
  // ...
  type InsightBySlugQueryVariables = Exact<{
    slug: Scalars['String'];
  }>;

  type InsightBySlugQuery = {
    readonly site: { readonly siteMetadata: { readonly title: string | null } | null } | null;
    readonly contentfulInsight: {
      readonly title: string | null;
      readonly content: { readonly raw: string | null } | null;
    } | null;
  };
}

What is wrong with my configuration?


Solution

  • To fix ERROR #12100 API.TYPESCRIPT.TYPEGEN, you need to change GraphQL query in gatsby-node.ts from

    const request = await graphql<Queries.Query>(`
      query AllInsights {
        allContentfulInsight {
          edges {
            node {
              title
              slug
            }
          }
        }
      }
    `);
    

    to

    const request = await graphql<Queries.Query>(`
      {
        allContentfulInsight {
          edges {
            node {
              title
              slug
            }
          }
        }
      }
    `);
    

    To fix Insight.tsx you need properly describe template props:

    import * as React from 'react';
    import { graphql } from 'gatsby';
    
    interface InsightTemplateProps {
      data: {
        site: {
          siteMetadata: { title: string };
        };
        contentfulInsight: {
          title: string;
          content: { raw: string };
        };
      };
    }
    
    const InsightTemplate: React.FC<InsightTemplateProps> = ({ data }) => {
      const title = data.contentfulInsight.title;
    
      return (
        <>
          <h1>{title}</h1>
        </>
      );
    };
    export default InsightTemplate;
    
    export const query = graphql`
      query InsightBySlug($slug: String!) {
        site {
          siteMetadata {
            title
          }
        }
        contentfulInsight(slug: { eq: $slug }) {
          title
          content {
            raw
          }
        }
      }
    `;