Part 4: Adding the category and home landing pages
Tags: aws, cloud, serverless, database
Published: 2021-01-17 23:15:44

<p>In this post we will add category and home landing pages to our blog. &nbsp;We will add HTTP 404 responses for invalid URLs as well.&nbsp;</p><p>This post picks up where we left off in <a target='_blank' href='https://www.jonholman.com/ComputelessBlog/part-3-adding-api-gateway-and-article-page'>Part 3: Adding API Gateway and the Article Page</a>. &nbsp;If you did not go through that post, you can get the project files we will be using as our starting point <a target='_blank' href='https://github.com/JonHolman/ComputelessBlog/tree/part-3-adding-api-gateway-and-article-page'>here</a>.</p><p>Let's start by adding some logic to handle requests for items that do not exist in our DynamoDB table. &nbsp;At the same time we will also create a variable ($type) that will help us later as we do things that are specific to the article page or the landing pages. &nbsp;Add the following to your page.yaml file after <code>#set($items = $input.path('$.Items'))</code> so this new content should start on line 3.</p>{% gist https://gist.github.com/JonHolman/775809936db67661c344ba2d27770353.js %}<p>In this new VTL code, the first thing we do is set a variable $count to the size of the array that is returned. &nbsp;We then have an if..elseif..else based on that $count variable. &nbsp;If $count is 0 we know our table did not have any data to return for that query, so we should respond with a 404 and a clear message to inform the user. &nbsp;Next we handle the condition where $count is 1 and the current path contains the entire sk of that 1 item. &nbsp;I added the and condition so that when we are just starting and only have one post so far, the home page does not end up using the article page template. &nbsp;Then we get to the else condition, all other combinations of data (count &gt; 1 or count is 1 but that 1 item's sk is not completely in the path) will have the $type variable set to category. &nbsp;We will use that $type variable throughout the rest of this post to handle items that are specific to one page type and not the other.</p><p>Next, let's set the page title based on certain conditions. &nbsp;I do not want the page title to always be the same thing. &nbsp;I like for the home page to have the base page title, then the category pages to have a page title to let you know you're on a category page, and then the article page titles can include the title of the article. &nbsp;To do that, replace the line <code>&lt;title&gt;W3.CSS Template - $items[0].heading.S&lt;/title&gt;</code> in our page.yaml file with the block below.</p>{% gist https://gist.github.com/JonHolman/84007891956f5ece3369f28216c4bd9e.js %}<p>You will notice in the code block above that we are not using the $type = category condition for the category page. &nbsp;We were unable to use that condition in this case because that same condition applies to the home page (as there are only two distinct page types article and category), all future uses of category will be identical. &nbsp;However in this case we are creating a distinction between the home page and a category page. &nbsp;So we are identifying the category page as not an article page and has more than two slashes in the context path.</p><p>Now we will introduce the changes to the main content section of the page, which will be different for article pages vs category landing pages (including the home page). &nbsp;The easiest way to make this update is to just replace that section of the page.yaml file and then we'll talk through what the new section is doing. &nbsp;The block below will replace from the line <code>&lt;!-- Blog entries --&gt;</code> to the line <code>&lt;!-- Introduction menu --&gt;</code>.</p>{% gist https://gist.github.com/JonHolman/7301edf0b4fe1db2511570aa40f0ab69.js %}<p>Now let's go through what we just changed. &nbsp;The main change here is creating a main content section for when $type is article and when it is not (landing pages). &nbsp;Another update is within the article page header we are now looping through the sk value to create links to any categories the article may be nested underneath. &nbsp;This will allow you to have many levels of categories, if you would like. &nbsp;We have also wrapped the article description and date in a condition to verify description is not empty, this is to cleanup for HTTP 404 response as I did not have any content I wanted to put in the description field for a 404 response.</p><p>The aspect that is very different from the landing pages to the article page is since the landing page will potentially have many articles to list, it is truncating each article to its first paragraph. &nbsp;That was the idea I came up with on how to easily give a preview of each article, open to other ideas.</p><p>Now we will update our openapi.yaml file. &nbsp;Our category landing page will be served by the route that we already defined in our openapi.yaml file. &nbsp;The route we defined in the previous post is using a greedy path that accepts everything after the slash for the API Gateway's root. &nbsp;So the category requests will go there as well. &nbsp;That is not a problem, the minor modification we need to make for the query to support the category requests will still allow the article requests to function properly. &nbsp;In our query to support that greedy path request, we will change it from looking for an exact match on pk and sk, to looking for a match on pk with an sk that begins with the provided path parameter. &nbsp;This will match the single article, when an article is requested, while still matching the entire category when a category is requested.</p><p>To make that change all you have to do in the openapi.yaml file is replace the line <code>'KeyConditionExpression': 'pk = :pk and sk = :sk',</code> with <code>'KeyConditionExpression': 'pk = :pk and begins_with(sk, :sk)',</code></p><p>Now for our final change for this post, to create a root route for the home page. &nbsp;As we discussed in <a target='_blank' href='https://www.jonholman.com/ComputelessBlog/part-2-planning-the-data-model'>Part 2: Planning the Data Model</a> our home page will use the access pattern 'Get all articles sorted by date' that is not limited to a single category. &nbsp;This will be achieved by leveraging the Local Secondary Index (LSI) we created on our DynamoDB table. &nbsp;An LSI uses the same pk, but a different sk. &nbsp;The sk for our LSI is date, so we can get all articles sorted by date. &nbsp;Add the following to the paths section of your openapi.yaml file, I like to put the root route as the first route in the paths section.</p>{% gist https://gist.github.com/JonHolman/2bbeae8853d7f7763f89a71d5603be5e.js %}<p>You will notice that this route's configuration is almost identical to the greedy route that we are using for everything else. &nbsp;The only differences are this is using another index, this is querying by only pk (to get all articles), and I specified False for ScanIndexForward so that the newest articles will be a the top of the page.</p><p>Now run sam deploy and wait for AWS SAM to deploy all of our updates. &nbsp;When it is complete you can browse directly to the root of our API Gateway endpoint (as it is output by the sam deployment). &nbsp;You will see that the home page is now working. &nbsp;You can also append a non-existing page to the URL to see the HTTP 404 response.</p><p>Our project directory at the end of this post is available <a target='_blank' href='https://github.com/JonHolman/ComputelessBlog/tree/part-4-adding-category-landing-and-home-page'>here</a>.</p><p>Thanks for reading. &nbsp;If you have any feedback or questions, please leave a comment or message me on Twitter/LinkedIn (links on the right side bar).</p>