简体   繁体   中英

How do I do dynamic routing with Next.js based on slug from GraphQL API?

I'm having a tough time getting my head around dynamic routing based on variables. I'm able to get a list of items in a collection but not an individual item and its fields for an individual page with a dynamic route with Next.js.

Background

I have a KeystoneJS headless CMS with a GraphQL API. I'm trying to create a simple blog with a list of posts and an individual post page. I have been able to query and return a list of posts, but I need to get an individual post based on the slug field so it can be accessed at /posts/[slug].js .

What I've tried

I've been using Apollo Client to handle the queries. I have an apolloClient.js file that connects to the API:

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

export default new ApolloClient({
  uri: "http://localhost:3000/admin/api",
  cache: new InMemoryCache(),
});

I have a post.service.js file to query the API:

// post.service.js
import { gql } from "@apollo/client";
import apolloClient from "../_utils/apolloClient";

export async function getAll() {
  return apolloClient
    .query({
      query: gql`
        query {
          allPosts {
            id
            term
            slug
          }
        }
      `,
    })
    .then((result) => result.data.allPosts);
}

export async function getBySlug(slug) {
  return apolloClient
    .query({
      query: gql`
        query {
          Post(slug: $slug) {
            id
            title
            lead
            body
          }
        }
      `,
    })
    .then((result) => {
      return result.data.Post;
    });
}

And finally, in posts/[slug].js I am trying to return the data like so:

//[slug].js
import Head from "next/head";
import { ApolloClient, InMemoryCache, gql } from "@apollo/client";
import { getAll, getBySlug } from "../../_services/post.service";

export default function Post({ post }) {
  return (
    <div>
      <Head>
        <title>Launchpad</title>
        <link rel="icon" href="/favicon.ico" />
      </Head>
      <main>
        <h1>{post.term}</h1>
      </main>
      <footer>
        <p>{post.lead}</p>
      </footer>
    </div>
  );
}

export async function getStaticPaths() {
  const posts = await getAll();

  const paths = posts.map((post) => ({
    params: { id: post.slug },
  }));

  return { paths, fallback: false };
}

export async function getStaticProps({ params }) {
  const post = await getBySlug(params.id);

  return { props: { post } };
}

Obviously, this doesn't work. I must not be passing the variable (which I'm assuming is the slug) into the query properly and having read several tutorials I still can't get my head around it. Does anyone see what I'm doing wrong?

In getStaticPaths the keys returned in the params objects need to match the dynamic route naming. In your case, since you're using posts/[slug].js for the route, you'd need to return params with the format { slug: post.slug } .

export async function getStaticPaths() {
  const posts = await getAll();

  const paths = posts.map((post) => ({
    params: { slug: post.slug }, // Rename to `slug`
  }));

  return { paths, fallback: false };
}

export async function getStaticProps({ params }) {
  const post = await getBySlug(params.slug); // Rename to `params.slug`

  return { props: { post } };
}

Edit: Regarding the request issue, the following change to getBySlug should make it work as expected.

export async function getBySlug(slug) {
  return apolloClient
    .query({
      query: gql`
        query Post($slug: String){
          post(slug: $slug) {
            id
            title
            lead
            body
          }
        }
      `,
      variables: {
        slug
      }
    })
    .then((result) => {
      return result.data.Post;
    });
}

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM