project / January 16th, 2021
This is a web app that can act as a personal blog or diary. The home page contains a list of blog posts, each displaying a title and a preview of the post content up to the first line break. Each post also contains links to a separate page to view the full post content. Additionally, the navigation bar also provides links to the about and contact pages. To compose blog posts, a hidden page is created that will contain forms to write and submit the post title and content. Because my portfolio site already contains a blog, this web app is not deployed and all pages and blog posts in this app simply use the Lorem Ipsum placeholder text.
The blog project acts as a "Boss Level" challenge for the EJS, Express, and Node modules from the Complete Web Development 2021 Bootcamp.
Originally, this was a "Boss Level" challenge to test my knowledge and skills from primarily the EJS module, but also relies on concepts that I've learned from Express and Node. The home page, blog posts, about page, and contact page are all generated using EJS partials. By using EJS to dynamically create new web pages for each blog post, I learned how easy it can be to avoid the repetition of having to create separate HTML files for each blog post by using templates. In the middle of this challenge, I take a break to learn about Express routing parameters and how they can be used in dynamic web page creation.
After learning about MongoDB and Mongoose, I revisited this app to apply this knowledge and store my blog posts in a MongoDB database.
EJS (Embedded JavaScript)
Node.js
Express.js
Express Routing Parameters
Lodash
MongoDB databases and Mongoose
The bootcamp has provided a starting file for the stylesheet, allowing my sole focus to be on creating the blog functionality with EJS templates in the original challenge. Later, I revisited this project and used MongoDB and Mongoose operations to store blog posts after learning about databases.
Originally, the blog post preview text in the home page was supposed to be truncated to 100 characters. I adjusted the code so that the preview text shows everything up to the first line break, in order to avoid cutting off the content mid-sentence. I also reversed the post order to display the new posts at the top of the home page, rather than the bottom. I also adjusted the CSS so that the footer would be fixed to the bottom of the page.
After setting the view engine using EJS so that static templates can be used in the project and converted to HTML files at runtime, dependencies are installed to set up the Express server. To complete the project, I proceeded to solve a set of 21 mini-challenges.
Challenges 1-5 required writing code to deliver variables that contained the starting content for the home page from the Express server to the EJS home template. Layout partials that contained EJS templates for the header (which contains a Bootstrap navigation bar and a custom CSS stylesheet) and footer are then created and applied to the home, about, and contact page templates. Once done, the app is run on a local host server and tested to ensure that each page is rendered properly with a header and footer.
Challenges 6-11 first focuses on fixing the navigation bar links to ensure that they make GET requests to the correct routes and renders the correct page, as clicking the links don't do anything at this point. Next, I create a new EJS template for the compose page, which contains a form with two inputs for the blog post title and content, and a submit button to publish the post. First, the text written in each input is accessed using the body-parser library and then logged in the console to ensure it's working. Once it is working, the input for the post content is changed to a textarea element, and Bootstrap classes are applied to style the inputs and button. Finally, instead of just logging the submitted pieces of data, they are stored in an object called post, and each post will be stored inside a global array called posts.
In challenges 12-17, the array of posts is passed over to the home template. At first, a FOR loop is written to iterate through each post and render the post title and content as HTML header and paragraph elements, respectively. Then, I learn about the forEach method which is an easier way to loop through each item in the array. At this point, I apply my own modification by using the unshift method to add new posts to the start of the array before it is passed to the home template, since I want the newest posts to be at the top of the page.
Before solving the remaining challenges, a quick lesson on Express Route Parameters is taught. In order to dynamically create a separate page for each blog post, routes with route parameters are used. According to the Express.js documentation, these parameters are defined after a colon in the route path to capture values at specific positions in a URL. Then, these parameters can be accessed from an object called params in the GET request.
In challenges 18-21, an IF statement is used with the forEach method to check if the route parameter is equal to the title of each post. If the parameter matches the post title, then the post is passed to the post EJS template and the app will render the post title and full post content. Thus, every time a new blog post is created, a brand new page for that page is also created. And because each page is created using the post template, they will all have the same formatting. The Lodash library is also used to convert the parameter and title to lower case before the IF expression.
One of the last challenges is to deal with an issue on the home page. While the titles (and links) for all blog posts are displayed in the home page, so is the entire content for each post. This is not ideal, since the home page will be cluttered with text if there are long posts. Next, Google and Stack Overflow are used to figure that the substring method is used to truncate the post content to 100 characters. While this did cut down the amount of space taken by each post on the home page, I was unsatisfied with how this method would cut off each post mid-sentence. So instead, I used the split method to first split the post content where a line break exists into separate strings and store them into an array. Then, I display just the first string of that array as a paragraph element under the post title. This solution looks cleaner and more professional, but also requires that the post author be aware that every part of the post up to the first line break will be displayed in the home page. They should compose their posts accordingly, and know that the first line break will mark the spot between the introductory or preview piece from the rest of the post. Finally, an additional link titled "Read More" is added for each post on the home page to give the user an extra way to access the full post, other than clicking the post title. The challenge is now complete!
The big drawback to the blog app after completing the challenge was its inability to persist post data after the Express server was restarted. After learning about Mongoose and MongoDB, I applied that knowledge to this blog in order to get it to persist data. After creating a blog database in MongoDB and a schema for the posts collection in that database, I used the save method to save each post object in the posts collection every time a POST request is made to the compose route. Next, whenever the home page is visited, the find method is used to fetch all the posts in the posts collection and place them in an array. The array of posts is then passed over to the home template for rendering. Here, I continued to use the unshift method to ensure the latest posts are added to the beginning of the array before they are passed to the home template.
Lastly, whenever a user clicks a post to see the full post content, the Mongoose findOne method is used render the correct blog post based on the post ID. After body-parser is used to pull the unique ID (which is automatically added to a database item by default) of the clicked post, findOne will attempt to match this ID to the ID of each post in the database. If there is an ID match, that post is pulled from the database and passed to the post EJS template for rendering. The user will now be able to view the full post of any post they click.
Similar to the To-Do List project, this challenge was an excellent experience in developing a better understanding of the full-stack web development process. It presented another opportunity to build a front-end with EJS templates, a back-end with MongoDB, and learn how the two parts are connected. Due to how "limited" I felt the project was, I did not deploy this app.
However, many of the ideas I had for improving the blog's styles were implemented in the blog component for my portfolio site. Although the technologies used to build each blog are different, the Portfolio site blog is essentially a version of this blog that meets its full potential.
The biggest improvement this blog needs is authentication and security, so that only authorized users can access the compose page. Currently, the blog can be accessed by anyone if they are aware of which URL to enter, which is a security issue.
Many of the ideas I had for improving this blog, were fully realized in the blog of this portfolio site. Check it out!