11

I am trying out Next.js and build a small app which fetches posts from a headless WordPress app with GraphQL installed. Then I use Apollo/Client to get GraphQL content:

apollo-client.js

import { ApolloClient, InMemoryCache } from "@apollo/client";

const client = new ApolloClient({
  uri: process.env.WORDPRESS_GRAPHQL_ENDPOINT,
  cache: new InMemoryCache(),
});

export default client;

In index I grab the posts:

index.js

import Head from "next/head";
import styles from "../styles/Home.module.css";

import { gql } from "@apollo/client";
import Link from "next/link";
import client from "../apollo-client";

function Home(props) {
  const { posts } = props;
  return (
    <div className={styles.container}>
      <Head>
        <title>Wordpress blog posts</title>
        <meta
          name="description"
          content="Wordpress blog posts with Apollo Client"
        />
        <link rel="icon" href="/favicon.ico" />
      </Head>

      <main className={styles.main}>=
        <div className={styles.grid}>
          {posts.map((post) => (
            <a
              key={post.node.databaseId}
              href={`/blog/${post.node.slug}`}
              className={styles.card}
            >
              <h2>{post.node.title}</h2>
              <div dangerouslySetInnerHTML={{ __html: post.node.excerpt }} />
            </a>
          ))}
        </div>
      </main>
    </div>
  );
}

export async function getStaticProps() {
  const { data } = await client.query({
    query: gql`
      query Posts {
        posts {
          edges {
            node {
              title
              databaseId
              slug
              excerpt(format: RENDERED)
            }
          }
        }
      }
    `,
  });

  if (data.posts.edges === 0) {
    return { notFound: true };
  }

  return {
    props: {
      posts: data.posts.edges,
    },
    revalidate: 10,
  };
}

export default Home;

Then for the single post page:

/blog/[slug].js

import Link from "next/link";
import { gql } from "@apollo/client";
import client from "../../apollo-client";

export default function BlogPage(props) {
  const { post } = props;

  if (!post) {
    return <p>Loading...</p>;
  }

  return (
    <div>
      <h1>{post.title}</h1>
      <div dangerouslySetInnerHTML={{ __html: post.content }} />
      <Link href="/">
        <a>&larr; back to home</a>
      </Link>
    </div>
  );
}

export async function getStaticProps({ params }) {
  const { slug } = params;
  const result = await client.query({
    query: gql`
      query GetWordPressPostBySlug($id: ID!) {
        post(id: $id, idType: SLUG) {
          title
          content
        }
      }
    `,
    variables: { id: slug },
  });

  if (!result.data.post) {
    return { notFound: true };
  }

  return {
    props: {
      post: result.data.post,
    },
    revalidate: 10,
  };
}

export async function getStaticPaths() {
  const result = await client.query({
    query: gql`
      query GetWordPressPosts {
        posts {
          nodes {
            slug
          }
        }
      }
    `,
  });

  return {
    paths: result.data.posts.nodes.map(({ slug }) => {
      return {
        params: { slug },
      };
    }),
    fallback: true,
  };
}

When adding a new post it works, once I delete it, it does not get removed. This happens both when doing npm run dev and npm run build then npm start

I might be getting something wrong here in how ISR and revalidate works. Or I might be missing something in my code? Any help would be appreciated.

-- edit --

Meanwhile there are a couple of more threads, here on Stackoverflow and the Next.js github repository, related to what I'm experiencing. Related pages:

Next.js does not delete dynamic page deleted in CMS

https://github.com/vercel/next.js/issues/25470

Next.js ISR page not being deleted after deleting it in CMS

How to clear NextJs GetStaticPaths cache / "unpublish" a dynamic route?

https://github.com/vercel/next.js/discussions/18967

juliomalves
  • 42,130
  • 20
  • 150
  • 146
Mark
  • 731
  • 2
  • 10
  • 29
  • Related to [Next.js does not delete dynamic page deleted in CMS](https://stackoverflow.com/questions/65615621/next-js-does-not-delete-dynamic-page-deleted-in-cms). Have you made sure the API response is not cached somehow? – juliomalves Jun 07 '21 at 17:02
  • Hmmm, unless the plugin WPGraphQL for WordPress has some weird caching going on, there should be no caching. That article might be somewhat related but still has no solution either. – Mark Jun 08 '21 at 02:43
  • Have you tried deleting the .next folder created by NextJs and then running `npm run dev` ? – Alvaro Castelan Jun 15 '21 at 02:20
  • I mean I could do that but the problem still remains. because basically what you're doing then is stopping the server, delete folder, restart. You can skip the delete part because then it'll work because site is being regenerated – Mark Jun 16 '21 at 16:44
  • Im running into basically the same issue, but I can't get it to update regardless of adding or deleting a post in my cms. Did you ever figure this out? – Spencer Bigum Oct 17 '21 at 02:53
  • @SpencerBigum I haven't really figured this one out really. For me it only happens on a local dev. Once I deploy it to Vercel I do not have this issue and revalidate works. Still, the problem is that revalidate is not being triggered on page load. So you'd have ro refresh the site/page 2 times in order to see the changes and revalidate has done it's thing. – Mark Oct 18 '21 at 01:10

2 Answers2

2

I am not sure if this matches your use-case, but in one of the projects I worked on, if the source data is deleted, we return a 404.

export async function getStaticPaths() {
  // Get the paths we want to pre-render based on posts
  // We do not need to generate anything during build
  return {
    paths: [],
    fallback: true,
  };
}

export async function getStaticProps({ params: { slug } }) {
  // Run your data fetching code here
  try {
    const data = await getData(slug);
    return {
      props: { data, slug },
      revalidate: 30,
      notFound: !data,
    };
  } catch (e) {
    return {
      props: { data: null, slug },
      revalidate: 30,
      notFound: true,
    };
  }
}

Docs: https://nextjs.org/blog/next-10#redirect-and-notfound-support-for-getstaticprops--getserversideprops

PsyGik
  • 3,535
  • 1
  • 27
  • 44
  • I don't think this will work nicely as `revalidate` will prevent `getStaticProps` from getting run for 30 seconds. Related question: https://stackoverflow.com/questions/68444505/how-to-clear-delete-cache-in-nextjs – Ciro Santilli OurBigBook.com Jan 02 '22 at 21:00
  • There is a bug. Scenario: -> change the slug to 'something'. -> go to localhost:3000/something -> change the slug to 'smt-else' -> go to localhost:3000/smt-else -> change the slug to 'something' The build will show notFound instead of the content – Elisei Nicolae May 29 '22 at 06:21
0

I think the problem is the fallback: true. You can find it in NextJS docs. In order to receive a 404 when you navigate to the deleted route you have to specify fallback: blocking

When using dynamic routes there are to ways to handle those paths that aren't in the getStaticPaths array. blocking or true.

The difference is that the first one works like getServerSideProps, so it's gonna fetch the data in the server, it will generate the HTML in the server an then return it, in future request it will serve the already static version of the page. This is the way you want to use if you want to return a 404 status code for those routes that were deleted.

Fallback: true works different. It serves a static version of the page, but you have to prepare that page to have a loader spinner or skeleton while fetch the data. If you prepare the page to do that, it will not return the 404 page even if you have a condition in your getStaticProps functions that return that. Actually, if you return only the notFound: true property, your page will throw an error because it's waiting for the props that will never come because it's returning only the notFound.

If you change the fallback: blocking, with run like getServerSideProps, will try to fetch the data, it will no exists because you previously deleted it, will return the notFound: true and with that the 404 page error. If you use fallback true it will try to serve the static page and then fetch the data.

Adrian Mole
  • 49,934
  • 160
  • 51
  • 83