Shingai Munyuki
Shingai Munyuki

Reputation: 611

Create dynamic routes by id from Next JS pages api

I have a page with a list of objects called stories that displays all my stories in an array. I also have a detail page with displays an individual story.

I want to click on a link on any given story on the list, then it will navigate me to the individual story. I want to use _id as my dynamic part of the URL, as shown in the GraphQL below.

My Graphql

export const listAllStories = () => {
  const query = gql`
    query StoryEntries($size: Int) {
      storyEntries(_size: $size) {
        data {
          _id
          _ts
          name
          premises{
            data{
              _id
              content
            }
          }
          createdAt
        }
      }
    }
  `

  return graphQLClient
      .request(query, { size: 999 })
      .then(({ storyEntries: { data } }) => data)
}

IN MY PAGES API I HAVE

export default async function handler(req, res) {
  const handlers = {
    GET: async () => {
      const storyEntries = await listAllStories()
      res.json(storyEntries)
    },
  }

  if (!handlers[req.method]) {
    return res.status(405).end()
  }

  await handlers[req.method]()
}

ON THE STORY LIST PAGE I HAVE

const ENTRIES_PATH = '/api/entries/allStories'

const useEntriesFlow = ({ initialEntries }) => {
    const { data: entries } = useSWR(ENTRIES_PATH, {
        initialData: initialEntries,
    })

    const EntryItem = ({ entry }) => (
         <>
            {entries?.map((entry) => (
                  {entry.name}
       <Link href="/story/[storyId]" as={`/story/${entry._id}`}>
                                <a>Go</a>
                            </Link>
             ))}
         </>
    )

export const getStaticProps = async () => ({
    props: {
        initialEntries: await listAllStories(),
    },
    revalidate: 1,
})

This is fine and works.

**AND THEN ON THE DETAIL PAGE FOR EACH INDIVIDUAL STORY [storyId].js I HAVE **

export default function Story({story}) {

    const router = useRouter()
    const storyId = router.query.storyId
    return(
        <>
            <h5>hello {story._id}</h5>
        </>
    )
}

export const getStaticPaths = async () => {
    const res = await fetch(`${server}/api/entries/allStories/`);
    const { data } = await res.json();
    const paths = data.map(story => {
        return {
            params: { id: story._id.toString() }
        }
          // trying to get the _id from each story 
    })
    return {
        paths,
        fallback: false
    }
}

    export const getStaticProps = async (context) => {
    const { storyId } = context.query;    // Your dynamic page is [storyId].js
    const server = "http://localhost:3000";

    const res = await fetch(`${server}/api/entries/allStories/${storyId}`);
    // trying to get the params._id from each story
    console.log(res)
    const { data } = await res.json();
    return {
        props: { story: data }
    }
}

ERROR

TypeError: Cannot read properties of undefined (reading 'map')

QUESTION

All I want to do is click on any story link, then it takes me to the details page, via the _id. I have tried a few things but I'm doing something (or some things) wrong.

Any help will be greatly appreciated.

EDIT AFTER. ERROR I'M GETTING. I'm not able to map my results on getStaticPaths

enter image description here

Upvotes: 1

Views: 7736

Answers (3)

dijux
dijux

Reputation: 571

// some helpful links
// https://nextjs.org/docs/basic-features/data-fetching#the-paths-key-required
// https://stackoverflow.com/questions/65783199/error-getstaticpaths-is-required-for-dynamic-ssg-pages-and-is-missing-for-xxx



export const getStaticPaths = async () => {
    const server = "http://localhost:3000";

    const data = await fetch(`${server}/api/entries/allStories/`).then(res => res.json() )

    const paths = data.map(({_id}) => ({
        params: { storyId: _id },
    }))

    return {
        paths,
        fallback: false
    }
}



export const getStaticProps = async (context) => {

    const storyId = context.params.storyId;    // Your dynamic page is [storyId].js
    const server = "http://localhost:3000";

    // const res = await fetch(`${server}/api/entries/allStories/${storyId}`);
    // trying to get the params._id from each story 
    // single api call (here)
    const res = await fetch(`${server}/api/entries/allStories/`);
    // removing const { data } because the data will be returned when calling res.json()
    const data = await res.json();
    // instead of the calling the single api (just a fix not recommended to access [0] directly )
    return {
        props: { story: data.filter(story => story._id === storyId)[0] }
    }
}

Upvotes: 0

HC0215
HC0215

Reputation: 519

export const getStaticProps = async (context) => {
    const { storyId } = context.query;    // Your dynamic page is [storyId].js
    const server = "YOUR SERVER VARIABLE";

    const res = await fetch(`${server}/api/entries/allStories/${storyId}`);
      // trying to get the params._id from each story
    const { data } = await res.json();
    return {
        props: { story: data }
    }
}

Upvotes: 1

Sadeeq Umar
Sadeeq Umar

Reputation: 1

uncomment

const router = useRouter()
const storyId = router.query.storyId

Upvotes: 0

Related Questions