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>← 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?
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
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.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.