In this video we will learn how to use Notion's client library to request block data and display the details of each of our recipes.
We will be pre-generating these static pages at build-time by using Next.js' getStaticProps
function, however, because our recipe IDs can be a nearly infinite number of options, we need to explicitly tell Next.js every possible option we would like to create a static page for.
In order to do this, we use Next.js' getStaticPaths
function. This allows us to make a request to the Notion API for all of our recipes, and provide Next.js with a finite collection of possible paths.
Instructor: [0:00] We now have each of our recipe titles displaying in our Next JS app. We want to emulate a little bit more of this behavior for our notion page. We want to be able to click on one of our recipes and see more information.
[0:10] We want to be able to see the ingredients and also the method. In order to do this, we need to modify our application slightly. Over in our getStaticProps function, we're only exposing each of our recipe's titles to our component.
[0:22] Let's extend that to return an object where the title can be this value. We also want to pass through an ID. This is going to be set to result.ID. Now up in our component, recipe isn't just a single string value. It's an object.
[0:37] We can say recipe.ID to confirm that's coming through. Great. Now we want to import Next's link component. Now rather than returning this paragraph, we are going to return a link with an anchor tag inside. Inside that we want to display our recipe.title.
[0:56] Now this is saying we're missing a HREF. Rather than putting the HREF on an anchor tag, in Next JS we put it on the link itself.
[1:03] We would like to navigate to /recipes/our ID. We've now got our recipes displaying as links, but they're all bunched up together. They're no longer block level elements. We can fix that by wrapping these in a paragraph tag.
[1:16] Because we're mapping over a collection here, we need to tell React a unique value to use as a key. Here we're going to use recipe.ID. Now if we click one of our recipe titles, we're going to be taken to a 404 page.
[1:28] That's because we haven't actually implemented our recipe details page yet. Let's create that page. Let's create a new folder in pages called Recipes. Inside that folder, we're going to create a new file for our recipe details page.
[1:40] The route we're currently looking at is /recipes/b321 blah blah blah. Now doesn't make sense for us to make a JavaScript route for this particular ID, and then a different ID, and then a different ID. The options for that recipe ID are nearly infinite.
[1:56] To handle this case in Next JS, we can use something called a dynamic route. We do that with the square brackets. We say square bracket ID.JS. Let's build out our component so we can see it on the page.
[2:07] We want to export out our getStaticProps function so our recipe details can also be static. In here, we would like to fetch details for recipe. Next JS has just hit the exact problem we were having earlier. We can't create a static page for an infinite number of possibilities.
[2:24] We need to provide Next JS with a finite number of options so it can create a static page for each of them. For that, we need to export another function called getStaticPaths. This is also an async function. In here we want to do very similar logic to our recipe's list.
[2:40] We want to create a new notion client and we want to make a request for all of the blocks in that page.
[2:46] This time, we want to build up an array of paths and we'll do that by iterating over each of our results. For each of those results, we again, want to check if the result.type is equal to child page. If that's the case, we would like to push a new value into our paths array.
[3:06] This is going to be an object with the key params, which is another object and we're going to set the ID to be result.ID. Now we need to return an object from our function. We'll return those paths and we'll also set the fallback option to false.
[3:23] Now, this option just enforces that if a user tries to navigate to a page that doesn't exist in our array of paths, it's going to display the 404 page. Next JS is now going to call our getStaticProps function for every possible path that we've provided it.
[3:38] In order to get the ID for our current recipe, we can de-structure params and then pull out the ID. For each of our recipes, we would like to display a title, a list of ingredients, and a list of steps in our method. In order to fetch this data from notion, we need to create a new client.
[3:57] Again, we need to supply our secret. To get a page's properties such as its title, we need to call notion.pages.retrieve, and tell it which page ID we would like the data for. In this case, that's going to be our ID that we got from the params.
[4:14] Now we can store that value in a variable called Page. Again, we can pass that data through to our component by returning an object with the key props. Eventually, we're going to pass through our recipe, but for now, we can just pass through page.
[4:28] If we come back up to our component declaration, we can then de-structure recipe, and again, display it in a pre-tag. We forgot to actually import the notion client library. These are all the properties we have access to on our page. Now we want to grab this title out of here.
[4:47] We're going to need to go result.properties.title.title, and then index zero to go into this array. Then we want to access .plain text. Let's do that in our getStaticProps function. We can pass that title variable through to our component just to make sure that we got the right value.
[5:07] That's looking good. Now this page variable contains the metadata for our page, but it doesn't contain the block data that we need for our ingredients and our method. We'll need to make a separate request for our blocks.
[5:17] Again, that block ID is just going to be set to ID. We can pass our blocks variable through to our component to see what it contains. You can see we have a heading one here for ingredients.
[5:29] Then we have a bulleted list item for one of our ingredients. Then we have another bulleted list item, another bulleted list item. If we scroll further down, we should find another heading for our method, and then a collection of numbered list items.
[5:43] This matches the structure of our notion page where we have our heading for ingredients and then a list of unordered items. We have our method and then an ordered list of steps. In order to get our ingredients and our method, we need to iterate over each one of our blocks.
[5:57] For each block, we want to check if it's type is a bulleted list item. If it is, then we want to push it into our list of ingredients. If we scroll back up to one of our bulleted list items, we can access that at block.BulletedListItem.text. Then again index zero and .plain text.
[6:20] We can copy that same logic for our numbered list item and instead push it into our method. Now, by passing through our title ingredients and method, we should see those in our Next JS application.
[6:38] If we go back and click a different recipe, we should see its title, ingredients, and method.
Thanks for letting me know! It looks like a few of the lessons' GitHub URL's are missing the branch. I will get this fixed up now!
In the mean time, here is the link for this one ๐
https://github.com/dijonmusters/notion-api-egghead-course/tree/master/03-request-block-data
All fixed now ๐ Thanks again for letting us know!
Error message: Invalid <Link> with <a> child. https://nextjs.org/docs/messages/invalid-new-link-with-extra-anchor
Still really enjoying the course.
Update for this section at: 1:24 Next.js no longer uses <a> tags inside links (Note: I'm doing a knitting pattern site instead of recipes)
<p key={pattern.id}> <Link href={`/patterns/${pattern.id}`}> {pattern.title} </Link> </p>
The Github link on this page is broken.