Build a Markdown-based Blog with Spring Boot - Part 5
Tags: java, springboot, thymeleaf, markdown
Published: 2021-07-01 06:36:54

<p style="clear: both; text-align: left;">Welcome to another instalment of the "Build a Markdown Blog with Spring Boot" series. You can find the previous parts right here: <a href="https://www.roshanadhikary.com.np/2021/05/build-a-markdown-based-blog-with-spring-boot-part-1.html">Part 1</a> | <a href="https://www.roshanadhikary.com.np/2021/05/build-a-markdown-based-blog-with-spring-boot-part-2.html">Part 2</a> | <a href="https://www.roshanadhikary.com.np/2021/05/build-a-markdown-based-blog-with-spring-boot-part-3.html">Part 3</a> | <a href="https://www.roshanadhikary.com.np/2021/05/build-a-markdown-based-blog-with-spring-boot-part-4.html">Part 4</a></p><h3 style="clear: both; text-align: left;">Recap</h3><p style="clear: both; text-align: left;">Right before this part, we have finished building how the blog works -- we use Markdown files stored in <em>resources/posts</em> directory to populate our database and then fetch the posts on respective requests using our <em>PostController</em> class.</p><p style="clear: both; text-align: left;">Now, we need to render the posts. For this, we use Thymeleaf and Bootstrap. We already have a Thymeleaf dependency specified in our POM.</p><h3 style="clear: both; text-align: left;">Bootstrap WebJar</h3><p style="clear: both; text-align: left;">For Bootstrap, we just need to add its WebJar in the <em>pom.xml</em> file.</p>
<p>
  {% gist https://gist.github.com/roshanadh/73dae7ae2d931f8d91c701b4bb21655d.js %}
</p>
<p style="clear: both; text-align: left;">Again, load the Maven changes in your POM file. Now we're ready to use Bootstrap in our Thymeleaf templates.</p><h3 style="clear: both; text-align: left;">Header for our templates</h3><p style="clear: both; text-align: left;">Inside the <em>resources/templates</em> directory, create a new template: <em>header.html</em>. This will be the header portion of our blog that will be present in every page.</p>
<p>
  {% gist https://gist.github.com/roshanadh/90fc0ee09ae8eeca8acfad6f8ac1cd75.js %}
</p>
<p style="clear: both; text-align: left;">Notice how we specify both <em>href</em> and <em>th:href</em> attributes when linking Bootstrap. This is the reason why Thymeleaf is a <em>natural</em> template engine: Templates written in Thymeleaf can be rendered with or without the server running. When we run the server, Thymeleaf reads the <em>th:href</em> attribute and links Bootstrap using the WebJar resource. When the server isn't running, the browser uses the <em>href</em> attribute to link Bootstrap.</p><h3 style="clear: both; text-align: left;">Creating a home page</h3><p style="clear: both; text-align: left;">Next, create a template, <em>posts.html</em>, that will serve as our home page.</p>
<p>
  {% gist https://gist.github.com/roshanadh/4a42ec315dc321d8db0581cca9e687dc.js %}
</p>
<p style="clear: both; text-align: left;">Here, for each post retrieved from the database, a card element is created. We also use the <em>#temporals</em>&nbsp;utility provided by Thymeleaf to format our <em>LocalDateTime</em> instance so that we can render the month name, day, and year in the format: <em>July 1, 2021</em>.</p><p style="clear: both; text-align: left;">You can additionally use a pagination element from Bootstrap to fetch paginated posts. (In this article, we will do it the manual way -- using request queries to specify the size and page.)</p><h3 style="clear: both; text-align: left;">The one where we render a post</h3><p style="text-align: left;">Now onto our <em>post.html</em> template. This will be the page that renders a single blog post in full.</p><p>
  {% gist https://gist.github.com/roshanadh/5593ecb3c9b485201174db80019a8134.js %}
</p>
<p style="text-align: left;">In this template, we employ some error-handling mechanism. Go back to the <em>PostController.getPostById</em> method. Notice how if a post by a given ID doesn't exist, we add an <em>error</em>&nbsp;attribute with <em>no-post</em>&nbsp;value in the model.</p><p style="text-align: left;">In <em>post.html</em>&nbsp;if the model contains the <em>error</em> attribute, we change the title as well as the body to indicate an error. Otherwise, we render the respective blog post.</p><p style="text-align: left;">This is it for the coding portion of this series. To test if the pagination works, add some more Markdown files in the <em>resources/posts</em> directory, and fetch them from the website.</p><h3 style="text-align: left;">Running the application</h3><p style="text-align: left;">Run the Spring Boot application and wait until the JVM starts running.</p><p style="text-align: left;">Visit <em>localhost:8080</em> to see your most recent blog posts.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-Ak5wrFMIkEw/YN1az2G2vdI/AAAAAAAAHX8/tHKmjbfdMyUDQZQ0OomA77UUTsfy-rPxgCLcBGAsYHQ/s1715/posts%2Bpage.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img alt="localhost:8080/posts renders a list of the most recent blog posts" border="0" data-original-height="955" data-original-width="1715" src="https://1.bp.blogspot.com/-Ak5wrFMIkEw/YN1az2G2vdI/AAAAAAAAHX8/tHKmjbfdMyUDQZQ0OomA77UUTsfy-rPxgCLcBGAsYHQ/s16000/posts%2Bpage.png" title="localhost:8080/posts renders a list of the most recent blog posts" /></a></div><br /><p style="text-align: left;">To modify the pagination parameters, we can use query strings in our request. For instance,&nbsp;<em>localhost:8080/posts?page=0&amp;size=3</em>&nbsp;should render the three most recent blog posts.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-78YGlsyO86Q/YN1bWSiGkBI/AAAAAAAAHYE/xXZFUDz9A7cdNaotrLHf8lC0tpQlfQUlACLcBGAsYHQ/s1919/three-posts.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img alt="Rendering three most recent blog posts using query strings" border="0" data-original-height="1020" data-original-width="1919" src="https://1.bp.blogspot.com/-78YGlsyO86Q/YN1bWSiGkBI/AAAAAAAAHYE/xXZFUDz9A7cdNaotrLHf8lC0tpQlfQUlACLcBGAsYHQ/s16000/three-posts.png" title="Rendering three most recent blog posts using query strings" /></a></div><br /><p style="text-align: left;">To render a blog post in full, we can use the ID of that post, or simply click the <em>Read more</em> hyperlink.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-IZzfZPXrV_E/YN1byh8klII/AAAAAAAAHYM/oAyPyr1M6BAIm8nW5ySJG-tdD7tLw5-dwCLcBGAsYHQ/s1713/post%2Bpage.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img alt="Rendering a full blog post" border="0" data-original-height="990" data-original-width="1713" src="https://1.bp.blogspot.com/-IZzfZPXrV_E/YN1byh8klII/AAAAAAAAHYM/oAyPyr1M6BAIm8nW5ySJG-tdD7tLw5-dwCLcBGAsYHQ/s16000/post%2Bpage.png" title="Rendering a full blog post" /></a></div><br /><h3 style="text-align: left;">Code</h3><p style="text-align: left;">We have successfully built the blog application that we set out to build. In the following article, we will see how we can deploy our application to Heroku, host our database on RemoteMySQL, and use Heroku's <em>Automatic Deployment</em> feature to update our blog whenever we add a new blog post.</p><p style="text-align: left;"><a href="https://github.com/roshanadh/markdown-blog/tree/part-5" rel="nofollow" target="_blank">This session's code</a> has been pushed to the GitHub repository; for previous sessions, <a href="https://github.com/roshanadh/markdown-blog/branches" rel="nofollow" target="_blank">check here</a>.</p>