I had no comments for a bit, but I felt like I had no idea what the reception of my articles was without having any place for people to leave comments. Occasionally people will leave useful critiques or tips on tutorials that can help future visitors as well, so I wanted to try adding something very simple back in.
I looked at all the options, but I really didn't want to invest in setting up some third party code that I couldn't rely on, or something with ads. So I figured I'd set one up myself. I designed the simplest possible comment system in a day, which this blog now runs on.
Here's some pros and cons to rolling your own comment system:
- No ads
- No third party scripts injected into your site
- Complete control over functionality and design
- Can be as simple or complicated as you want
- Little to no spam because spambots aren't set up to spam your custom content
- Easy to migrate – it all exists in one Heroku + Postgres server
- More work to set up
- Less features
- Need to set up manual anti-spam measures and moderation
If you've also struggled with this and wondered if there could be an easier way, or are just intrigued to see one person's implementation, read on!
This guide will not be a full, guided walkthrough – however, all the steps to create this are documented from start to finish in Create and Deploy a Node.js, Express, & PostgreSQL REST API to Heroku. The comments API is a Node + Express server connected to a Postgres instance hosted for free on the hobby tier of Heroku (Hopefully I don't go over the 10,000 row limit any time soon). A combination of that article and what I've documented here can get you all the way to having your own comment system.
Note: Comments overall aren't a big deal to me, so I don't care if I'm just running some little hobby API I created, or if it goes down for any reason. I think it should be pretty solid, but obviously if your needs are more professional than mine, you should go ahead and buy Disqus or something.
The comments API consists of three parts:
The frontend is written for React, but if you know how to make a form and an API call, it can be easily adjusted to whatever static system you're using.
The first step assumes we'll be setting up a Postgres database called
comments_api with a
comments_api database, I created a
comments table, with
slug refers to the article URL – so for
https://example.com/how-to-bake-a-cake, the slug would be
how-to-bake-a-cake. Finally, I added
parent_comment_id in case you want to have the ability to reply to comments.
You could probably get more fancy with it and add website, email, upvotes and other features, but I just wanted it to be simple. I'm not adding in any login or OAuth/user authentication either, which makes it even more simple, but comes with the drawbacks of an anonymous online system.
In Create and Deploy a Node.js, Express, & PostgreSQL REST API, I document how to set up an Express server and make a Postgres pool connection.
The aforementioned article goes much deeper into production level concerns of a Node.js server, such as error handling, validation, and brute force rate limiting.
In our simplified, development example setup, we'll require
express, a Node.js server, plus
cors to allow our app to parse and request the data, and
pg to create a Postgres pool connection.
This article is using default values for the Postgres connection –
passwordas password, etc.
Remember that this example is simply for demonstration purposes and development.
Get all comments
First, I want a
GET query that will just return everything to Node.js, ordered by date. This is just for me to have, so I can easily review all comments.
Get comments by page slug
More importantly, I want a query that will only return the comments that match the current page's slug. This is the query I'll use for each article.
Create a comment
Add the ability to
POST a new comment, which people will be able to do through the HTML form.
Update an existing comment
As moderator, I want the ability to update an existing comment. Commentors won't be able to edit their comments, because they're all anonymous. This will be a protected endpoint.
Delete a comment
Another protected endpoint, only I will have the ability to delete a comment.
Putting it together
We have our two
axios call to your comment API, and save the data in state somewhere. Once I retrieve the JSON response from the API server, which will be an array of comment objects, I can pass it to wherever I'm displaying the comments.
Sorry, I'm not using hooks yet. It's okay, deep breath. We'll get through this.
In this case, that will be a
Comments component will contain both the form to submit a comment, and the list of existing comments if there are any. So in state, I'll save the comments list, and an object to store new comment state for the form.
I'll admit this code is not the most pristine I've ever seen, but as I mentioned, I wrote the thing in a day, so feel free to refactor and write however you want.
When a comment is submitted, I'll use
fetch once again, this time with the
post method. If everything went through correctly, append the new comment to the comments array, and reset the new comment.
I'll also have an
onChange handler for the form.
We can start the render lifecycle now.
I made some simple error or success messages to show after submitting the form.
The comment form only consists of name and comment in my case, as I decided to go the Sivers route and only allow comment replies by yours truly on the site.
Finally, we'll display the form and the comments. I decided to either display the form or a success/error message. A visitor won't be able to leave two comments in a row without reloading the page.
After that, it's just a matter of looping through the comments and displaying them. I've made comment replies incredibly simple – only one reply allowed per post, and no nesting.
You'll probably also want to add in some anti-spam moderation system, like adding a
moderated column to the comments, setting it to
false by default, and manually setting it to
true if you approve the comment.
I hope this helps out someone who wants a simple, free system for their own personal site. I like reinventing the wheel and making things from scratch. It's fun, and I learn a lot.
For more information on building contact forms with Gatsby, check out the docs reference guides.