Search code examples
reactjsnext.jsapollo

Using initialApolloState on a SSG NextJS site with dynamically created paths


I've reconfigured my app based on this official Vercel starter example and I've got it to the point where I have my data cached and set to initialApolloState from performing the queries in getInitialProps, but I'm having trouble figuring out how to get the extract the data from this object on each page.

My pages are dynamically created from getStaticPaths which queries a GraphQL API that is set up to query REST API endpoints. In my old configuration (which did not allow for SSG, which is why I had to reconfigure), the getStaticProps function used the page ID parameter to query data with that specific ID and get all the data needed for that page and set them to props. This new setup however, seems to set the data to the initialApolloState object with the ID attached to the object name itself.

For example, one of the prop objects is named TourDossier:33887, so I don't know how I'm supposed to extract any data from this object when it will have a unique name on each page. The page props have unique names on each page, as the ID is attached to the props themselves, which makes it impossible to access this data in a general way.

I suppose I'm just lost on how I'm supposed to extract data when using Apollo and SSG using the official example I listed above. The example is very simple and there seems to be another layer of complexity when using getStaticPaths and the ID parameter from the path in the getStaticProps query.

Any help or docs to read would be greatly appreciated.

edit: here is my dynamically created page(s) which use getStaticPaths and getStaticProps

import { initializeApollo } from "../../apollo/client";
import NavBar from "../../widgets/NavBar";
import { useRouter } from "next/router";
import { useQuery } from "@apollo/client";
import {
    GET_TOURS,
    GET_TOUR_DOSSIER,
    GET_ITIN_DOSSIER,
    GET_MAP_ROUTES,
    GET_MAP_ACTIVITIES,
    GET_MAP_TRANSPORT,
    GET_MAP_ACCOM,
} from "../../util/gql";
import TourMain from "../../widgets/TourMain";

const Tour = (props) => {
    const router = useRouter();

    // This returns 'undefined'.
    const data = useQuery(GET_TOUR_DOSSIER);

    return (
        <div>
            <NavBar />
            // Passing these as props does not work since these objects
            // all have unique IDs based on the query (eg: tourDossier:33887)
            <TourMain
                tour={initialApolloState.tourDossier}
                itin={initialApolloState.itinDossier}
                mapRoutes={initialApolloState.mapRoutes}
                mapAccom={initialApolloState.mapAccom}
                mapActivities={initialApolloState.mapActivities}
                mapTransport={initialApolloState.mapTransport}
            />
        </div>
    );
};

export async function getStaticPaths() {
    const apolloClient = initializeApollo();

    const tours = await apolloClient.query({
        query: GET_TOURS,
    });

    const paths = tours.data.tours.map((tour) => `/tours/${tour.id}`);

    // { fallback: false } means other routes should 404.
    return { paths, fallback: false };
}

export async function getStaticProps({ params }) {
    const apolloClient = initializeApollo();

    const tourVars = { id: params.id };
    const tourDossier = await apolloClient.query({
        query: GET_TOUR_DOSSIER,
        variables: tourVars,
    });

    const itinVars = { id: tourDossier.data.tourDossier.itinerary[0].id };

    await apolloClient.query({
        query: GET_ITIN_DOSSIER,
        variables: itinVars,
    });

    await apolloClient.query({
        query: GET_MAP_ACCOM,
        variables: itinVars,
    });

    await apolloClient.query({
        query: GET_MAP_ACTIVITIES,
        variables: itinVars,
    });

    const mapTransport = await apolloClient.query({
        query: GET_MAP_TRANSPORT,
        variables: itinVars,
    });

    // Parse mapTransport array to coordinate string which can be consumed
    // by Mapbox Directions API.
    const coords = mapTransport.data.mapTransport.map((route) =>
        route
            .reduce(
                (str, place) =>
                    `${str};${place.coordinates[0]},${place.coordinates[1]}`,
                ""
            )
            .substring(1)
    );
    const routesVars = { coords };
    await apolloClient.query({
        query: GET_MAP_ROUTES,
        variables: routesVars,
    });

    // Pass tour and itinerary data to the page via props.
    return {
        props: {
            initialApolloState: apolloClient.cache.extract(),
        },
    };
}

export default Tour;

I'm not sure if I'm supposed to be querying the cache in order to get page props or I'm somehow supposed to be able to extract the data from initialApolloState even though the objects all have unique names based on the ID of the query.


Solution

  • You need to make the same queries (including variables) in both getStaticProps and React component.

    Apollo caches also on query AND variables, so query with different variables has different cache.

    Change useQuery to include variables same as query in getStaticProps.

    const data = useQuery(GET_TOUR_DOSSIER, {
      variables: { id: router.query.id } 
    });