Skip to content

nathangero/tech-blog

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

81 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Tech Blog

Description

The purpose of this repo is to make a CMS-style blog focused on tech! It's a place where a user can view other blogs and comments, and even add their own posts and comments once signed up and logged in. This website is optimized for both mobile and desktop screens. The homepage shows all the blog posts on the website, the dashboard shows the logged in user's own posts, and clicking on a post will show the comments on a blog post.

Deployed website: The Tech Blog

Tech Stack and Why

Tech Docs
Javascript docs
CSS docs
Bulma framework docs
Express JS docs
Express Handlebars docs
Handlebars docs
Express Session docs
Sequelize v6 docs
Bcrypt docs
Heroku docs

I wanted to create a full-stack web application and deploy it to a server, and in this case it's Heroku. Using Express.js to send/retreive data from the JAWSDB SQL via an api endpoint was nice and simple, and then calling res.render() with that data to go to a webpage was simple too.

Using handlebars to render the pages also made it nice and simple to direct the user to certain pages. Using handlebars' helps modularize the code with its use of partials and helper functions like {{#each}}. I used res.render() from the server and document.location.replace() from JavaScript files to move the user to specific pages depending on what they were doing.

Examples:

  • When the user clicks on Dashboard but isn't logged in, the click sends a request to the server where it checks if the user is logged in or not. If they're logged in, the server directs them the Dashboard page, if not then they'll be redirected to the Login page.
  • When a logged in user adds a comment, the server will send back a 200 response instead of a webpage to render. This is because the page where the comment was added doesn't change. So, when the JavaScript function that called the api endpoint receives a 200 response, it will just reload the page.

Database Breakdown

There are 3 tables/models I defined:

User
id int, not null, auto increment primary key
username string, not null
password string, not null
Post
id int, not null, auto increment primary key
title string, not null
content string, not null
user_id string, not null, references to User
Comment
id int, not null, auto increment primary key
content string, not null
user_id string, not null, references to User
post_id string, not null, references to Post

Relationships

  • A User has many Posts and Comments
  • A Post has many Comments
  • A Post belongs to a User
  • A Comment belongs to a User

Usage

On the website you can do the following:

  • View blog posts and comments on any blog
  • Sign Up
  • Log in. Below is what's available after logging in
    • View your blog posts
    • Update or delete your blog posts
    • Comment on other blog posts
    • Logout

When making or updating a post, you must fill out both the Title and Content fields.

When making a comment, you must fill the Content field.

Learning Points

  • Using sequelize.literal() and knowing how the JSON is returned is so important. When getting all Posts and their Comments, I wanted to get the username for each comment. I used sequelize.literal() but the WHERE clause wouldn't work until I did WHERE comments.user_id. I was doing comment.user_id and not realizing that the JSON response had the Post's comments listed as comments as opposed to comment. This caused a lot of headache, but I know now how it works! You can see my code below
  • Using Bulma was easier to work with than Bootstrap surprisingly enough. I had used Bulma in a previous project called Food Finder, but I didn't think much of it outside of "let's try another framework." In my opinion, the Bulma class names are much easier to understand and memorize, despite their long nature. That's ultimately why I used Bulma in this project than Bootstrap.
  • If two elements share the same HTML id, they canNOT share the same event listener. Only the first one delcared in the HTML will get the listener attached. So, I had to create two different ids for the Logout button.
  • Using role="button" tag for a <a> tag is really helpful for navigation bars. It let me keep the style of the nav bar elements but giving me the attributes of a button like .addEventListener
  • Using Heroku's JAWSDB was very easy to deploy to! Once I set it up in my Heroku project's settings, I just added this code and it all worked!
if (process.env.JAWSDB_URL) {
    sequelize = new Sequelize(process.env.JAWSDB_URL);
}

Code Snippets

Nested Eager Loading

Nested findAll() call to get all Posts with their Comments, and the Comments with the User that created them.

const data = await Post.findAll({
    include: [
        {
            model: Comment,
            attributes: [
                "id",
                "content",
                "createdAt",
                [sequelize.literal(
                    `(SELECT user.username FROM user WHERE comments.user_id = user.id)`
                ), "comment_author"],
            ]
        },
    ],
    attributes: [
        "id",
        "title",
        "content",
        "createdAt",
        "updatedAt",
        [sequelize.literal(
            `(SELECT user.username FROM user WHERE post.user_id = user.id)`
        ), "post_author"],
    ]
});

Custom Handlebar Helper

Using custom handlebar helpers to show if a user can add a comment to a post

canAddComment(isLoggedIn, fromHomepage) {
    return isLoggedIn && fromHomepage;
}
{{#if (canAddComment loggedIn fromHomepage)}}
  <button id="button-make-comment" class="button">Add Comment</button>
{{/if}}

Idle Timer

Code for making an idle timer that's called in the comment.js, post.js, and update-post.js files.

function setupIdleTimer() {
    let count = IDLE_TIME;
    const timer = setInterval(async () => {
        if (count === 0) {
            clearInterval(timer);

            await fetch("/api/users/logout", {
                method: "POST",
                headers: {
                    "Content-Type": "application/json",
                },
            });

            document.location.reload();
            return;
        } else {
            count--;
        }

    }, COUNTDOWN_INTERVAL);
}

Images

Homepage on desktop screen

Homepage on a desktop screen showing blogs from users


New Post screen

New Post screen with title and content fields to fill out


Homepage on mobile screen

Homepage on a mobile screen showing blogs from users

Future Ideas

  • Adding a click count to each post. So, adding a new column in the db to account for the amount of times a blog has been clicked on.
  • Showing how many comments a blog post has on the Homepage and Dashboard

Key Takeaway

Full-stack web development is fun! I had a blast making this even though it was really frustrating at times. Using express.js with handlebars to render webpages from a server was pretty interesting, and makes me think how big production websites work. I know there's a multitude of tech stack combinations out there, and I'm interested in learning more of them!

Credits

Sequelize nested eager loading

Passing in a parameter in event listener

Using .svg file as favicon

My SVG Generator

Resources

cookie secure help

Potential heroku cookie secure

Bulma colors