Creating Dynamic Routes using 'getStaticPaths' and 'getStaticProps' in NextJS

Viewed 4452

I am trying to create dynamic pages that show individual book details (.i.e. title/author) on a separate page based on a query string of the "id" for each book. In a previous question I asked, answers from users were very helpful and I have a much better understanding of how to use getStaticPaths and getStaticProps correctly. However, I am not quite there in my code for how to do this.

Here is the basic setup and context.

  1. I am running NextJS 9.4 and would like to use a API endpoint instead of querying the database directly.
  2. The book data is being pulled from a MongoDB Atlas Database and uses Mongoose
  3. Documents in the MongoDB have a "_id" as a unique ID.
  4. I have tried to incorporate and learn from existing Github examples and NextJS documentation but I still get the following error.

Error: A required parameter (id) was not provided as a string in getStaticPaths for /book/[id]

Here is the code I have so far. I have tried to keep the code as clean as possible for now.

export default function Book({ book }) {
    return (
        <article>
            <h1>Book Details Page</h1>
            <p>{book.title}</p>
            <p>{book.author}</p>
        </article>
    )
}

export async function getStaticPaths() {
    
    const url = `${baseUrl}/api/books/books`

    const response = await axios.get(url);

    const books = response.data

    const paths = books.map((book) => ({
        params: { id: book.id },
    }))
    
    return { paths, fallback: false }
}

export async function getStaticProps({ params }) {
    const url = `${baseUrl}/api/books/books/${params.id}`
    const res = await axios.get(url)
    const book = await res.json()

    return { props: { book }}
}

The API endpoint looks like this:

import Book from '../../../models/Book';
import dbConnect from '../../../utils/dbConnect';

// conects to the database
dbConnect();

// This gets all the book from the database
export default async (req, res) => {
    const books = await Book.find()
    res.status(200).json(books)
}

Any support or feedback would be greatly appreciated. Once I get this working, I can hopefully be able to understand and help assist others in creating dynamic routes with NextJs. Thank you.

2 Answers

You can't make calls to Next.js API routes inside getStaticProps or getStaticPaths. These functions are executed at build time, so there is no server is running to handle requests. You need to make request to DB directly.

If you want to keep it clean you could create a helper module like allBooksIds() and keep DB query in a separate file.

See the same issue - API call in NextJS getStaticProps

Simply add toString() method in getStaticPaths because the book id is of type ObjectID("ID") if you do params: { id: book._id.toString() } it will convert ObjectID("ID") to type string which is accepted by getStaticPaths().The complete code for the nextjs part is below also update your API route as follows :-

The upper one is the API route the bellow one is Nextjs Page

import Book from '../../../models/Book';
import dbConnect from '../../../utils/dbConnect';

// conects to the database
dbConnect();

// This gets all the book from the database
export default async (req, res) => {
    const books = await Book.find({})
    res.status(200).json(books)
}

export default function Book({ book }) {
    return (
        <article>
            <h1>Book Details Page</h1>
            <p>{book.title}</p>
            <p>{book.author}</p>
        </article>
    )
}

export async function getStaticPaths() {
    
    const url = `${baseUrl}/api/books/books`

    const response = await axios.get(url);

    const books = response.data

    const paths = books.map((book) => ({
        params: { id: book._id.toString() },
    }))
    
    return { paths, fallback: false }
}

export async function getStaticProps({ params }) {
    const url = `${baseUrl}/api/books/books/${params.id}`
    const res = await axios.get(url)
    const book = await res.json()

    return { props: { book }}
}

Hope this is helpful

Related