reactjsgraphqlvitebad-requesthygraph

Bad request 400 using Vite React HygraphCMS


In the following code, I run into the issue that I get a 400 BAD REQUEST and pageData doesn't load. When I simply remove the GridText module the code is working fine though. ChatGPT and I are running out of ideas. I hope someone can help.. I added GridText exactly as I did for the other modules, the API looks correct when I test it in the env link.

[slug].tsx

import React, { useEffect, useState } from "react";
import { useParams } from "react-router-dom";
import { GraphQLClient, gql } from "graphql-request";
import NotFound from "./NotFound";
import "../styles/index.css";
import Nav from "../modules/Nav/index";
import { navigationQuery, NavigationResponse } from "../modules/Nav/fragment";
import Footer from "../modules/Footer/index";
import { footerQuery, FooterResponse } from "../modules/Footer/fragment";
import TextImage from "../modules/TextImage/index";
import {
  TEXT_IMAGE_FRAGMENT,
  TextImageFields,
} from "../modules/TextImage/fragment";
import Hero from "../modules/Hero/index";
import { HERO_FRAGMENT, HeroFields } from "../modules/Hero/fragment";
import GridTour from "../modules/GridTour/index";
import {
  GRID_TOUR_FRAGMENT,
  GridTourFields,
} from "../modules/GridTour/fragment";
import GridText from "../modules/GridText/index";
import {
  GRID_TEXT_FRAGMENT,
  GridTextFields,
} from "../modules/GridText/fragment";

// Define the structure of the page data
interface PageData {
  title: string;
  modules: Array<
    {
      identifier: string;
    } & (TextImageFields | HeroFields | GridTourFields | GridTextFields)
  >;
}

// Define the expected shape of the GraphQL response
interface GraphQLResponse {
  page: PageData;
}

// Initialize the GraphQL Client with the endpoint from Vite's environment variable
const graphConnect = new GraphQLClient(import.meta.env.VITE_ENDPOINT);

// GraphQL query to fetch page data by slug
const pageQuery = gql`
  query ($slug: String!) {
    page(where: { slug: $slug }) {
      title
      modules {
          ...TextImageFields
          ...HeroFields
          ...GridTourFields
          ...GridTextFields
        }
      }
    }
  }
  ${TEXT_IMAGE_FRAGMENT}
  ${HERO_FRAGMENT}
  ${GRID_TOUR_FRAGMENT}
  ${GRID_TEXT_FRAGMENT}
`;

const Page: React.FC = () => {
  const { slug } = useParams<{ slug: string }>(); // Get slug from URL params
  const [pageData, setPageData] = useState<PageData | null>(null); // State to hold page data
  const [navigationData, setNavigationData] =
    useState<NavigationResponse | null>(null); // State for navigation data
  const [footerData, setFooterData] = useState<FooterResponse | null>(null); // State for footer data
  const [loading, setLoading] = useState(true); // Loading state
  const [error, setError] = useState<string | null>(null); // Error state

  useEffect(() => {
    const fetchData = async () => {
      setLoading(true); // Start loading
      setError(null); // Reset error state

      try {
        // Fetch page data
        const response = await graphConnect.request<GraphQLResponse>(
          pageQuery,
          {
            slug,
          }
        );

        if (!response.page) {
          throw new Error(`No page found for slug: ${slug}`);
        }

        setPageData(response.page); // Set page data

        // Fetch navigation data
        const navigationResponse =
          await graphConnect.request<NavigationResponse>(navigationQuery);
        setNavigationData(navigationResponse);

        // Fetch footer data
        const footerResponse = await graphConnect.request<FooterResponse>(
          footerQuery
        );
        setFooterData(footerResponse);
      } catch (err: any) {
        // Log GraphQL specific error response if available
        if (err.response && err.response.errors) {
          console.error("GraphQL Response Error Details:", err.response.errors);
        } else {
          console.error("Unexpected error:", err);
        }

        setError("Failed to load data."); // Set error message
      } finally {
        setLoading(false); // End loading
      }
    };

    fetchData();
  }, [slug]); // Run effect when slug changes

  if (loading) {
    return <div>Loading...</div>; // Show loading state
  }

  if (error) {
    return <div>{error}</div>; // Show error message
  }

  if (!pageData) {
    return <NotFound />; // Show 404 if no page data
  }

  return (
    <>
      {navigationData && <Nav data={navigationData.navigation} />}

      <div className="relative top-16">
        {pageData.modules.map((module) => {
          if (!module.id) {
            return null;
          }
          switch (module.identifier) {
            case "TextImage":
              return (
                <TextImage key={module.id} data={module as TextImageFields} />
              );
            case "Hero":
              return <Hero key={module.id} data={module as HeroFields} />;
            case "GridTour":
              return (
                <GridTour key={module.id} data={module as GridTourFields} />
              );
            case "GridText":
              return (
                <GridText key={module.id} data={module as GridTextFields} />
              );
            default:
              return null;
          }
        })}
      </div>

      {footerData && <Footer data={footerData.footer} />}
    </>
  );
};

export default Page;

GridText index.tsx

import React from "react";

interface HeadlineData {
  size: string;
  text: string;
}

interface GridTextProps {
  data: {
    headline?: HeadlineData | null;
  };
}

const GridText: React.FC<GridTextProps> = ({ data }) => {
  return (
    <div>
      <p>{data?.headline?.text}</p>
    </div>
  );
};

export default GridText;

GridText fragment.ts

import { gql } from "graphql-request";

export const GRID_TEXT_FRAGMENT = gql`
  fragment GridTextFields on GridText {
    id
    identifier
    headline {
      size
      text
    }
  }
`;

export interface GridTextFields {
  id: string;
  identifier: string;
  headline: {
    size: string;
    text: string;
  } | null;
}

This is the full error message:

Error fetching data: _ClientError: GraphQL Error (Code: 400): {"response":{"status":400,"headers":{}},"request":{"query":"\n  query ($slug: String!) {\n    page(where: { slug: $slug }) {\n      title\n      modules {\n        ... on TextImage {\n          ...TextImageFields\n        }\n        ... on Hero {\n          ...HeroFields\n        }\n        ... on GridTour {\n          ...GridTourFields\n        }\n        ... on GridText {\n          ...GridTextFields\n        }\n      }\n    }\n  }\n  \n  fragment TextImageFields on TextImage {\n    id\n    identifier\n    headline\n    image {\n      url\n      alt\n    }\n    paragraph {\n      text {\n        html\n      }\n    }\n  }\n\n  \n  fragment HeroFields on Hero {\n    id\n    identifier\n    imageLarge {\n      url\n      alt\n    }\n    links {\n      ...PagelinkFields\n      ...URLFields\n    }\n    paragraph {\n      text {\n        html\n      }\n    }\n    imageSmall {\n      url\n      alt\n    }\n    headline {\n      size\n      text\n    }\n  }\n  \n  fragment PagelinkFields on Pagelink {\n    identifier\n    id\n    text\n    page {\n      slug\n    }\n  }\n\n  \n  fragment URLFields on URL {\n    identifier\n    id\n    url\n    text\n  }\n\n\n  \n  fragment GridTourFields on GridTour {\n    id\n    identifier\n    headline {\n      size\n      text\n    }\n    gridTourSection {\n      id\n      headline\n      guidedTour {\n        slug\n        id\n        bookingForm\n        coverImage {\n          url\n          alt\n        }\n        description\n        distance\n        duration\n        highlights\n        images {\n          id\n          url\n          alt\n          width\n          height\n        }\n        title\n      }\n    }\n  }\n\n  \n  fragment GridTextFields on GridText {\n    id\n    identifier\n    headline {\n      size\n      text\n    }\n  }\n\n","variables":{"slug":"home"}}}
at runRequest (graphql-request.js?v=51386c9c:7767:12)
at async GraphQLClient.request (graphql-request.js?v=51386c9c:7962:22)
at async fetchData ([slug].tsx:91:26)

Solution

  • When I look at the error message Error fetching data: _ClientError: GraphQL Error (Code: 400), this tells me it's a GraphQL query syntax error. Meaning, the GraphQL query you're sending has an issue. Not sure it's related to GridText.

    I would suggest to isolate the GraphQL query you're sending from your React.js code, and make sure the query is correct. The right tool for this is a GraphQL playground, e.g. if you're serving your GraphQL server with Apollo, what I'm talking about would be at this address http://localhost:4000/graphql

    This will allow you to iterate on your query syntax and eliminate this as the source of the issue.