H11Y BLOG Logo

Adding a Like Button to The Blog

7/4/2023

I started this blog a little less than a year ago as a DIY coding project.

As of today I now have the capability of having “likes” on each blog post. What a simple little thing to hook up… Maybe if you were using a full framework like Django. But I’ve kept things real simple… a compiled static website.

I was inspired by Josh W Comeau’s blog. He gave a talk at SmashingConf which I had the priviledge to see in San Francisco last month. He gave a talk about making a very whimsical like button. Mine isn’t very sophisticated… but it works.

So what’s involved with adding a like button to a blog?

  1. Database schema
  2. Database hosting
  3. Server for interacting with the database
  4. Server hosting
  5. DNS setup for the server so that CORS isn’t an issue.
  6. Client side interaction with server.
  7. UI element for client interaction.

Database Schema

I’ve been wanting to learn how to use sqitch for managing my database integrations. I basically followed this tutorial halfway through to make a database schema and deploy it.

Ultimately we want a table that we can query that looks like this:

CREATE TABLE aimez.likes (
    id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
    slug  TEXT        NOT NULL,
    username  TEXT        NOT NULL,
    timestamp TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

Database Hosting

I have really enjoyed using flyio for hosting servers. It’s so simple to deploy a server. It’s a little more involved, but still pretty simple to setup a database conection for the server. First you setup a Node app and request a pg instance in the process.

After running fly launch, a fly.toml is automatically created.

Then you need to make a new fly.toml specific to the database application on fly.io to set up an external connection to the postgres instance. By default, the postgres database is only accessible through a private subnet. This will result in the ability to deploy changes with a one liner sqitch deploy <flyio dev pg connection string> right from your dev box. Easy, but maybe a little bit insecure. Oh well, this is a hobbyist blog.

Server Coding

Starting with a simple server template that I’ve made in the past…

Chat GPT did a pretty good job writing up an Express server for interacting with the database schema from step 1.

Prompt:

Create an express application that has an insert POST endpoint and a GET endpoint. The insert expects a request body of slug and username. The GET endpoint expects a slug parameter and will return the total number of rows with that slug.

The interaction will be from https://blog.h11y.com and https://likes.h11y.com. So CORS needs to be configured. Using cors middleware.

Testing against production on localhost

Avoid CORS issue hitting https://production.com from localhost with a proxy server.

const express = require('express');
const cors = require('cors');
const { createProxyMiddleware } = require('http-proxy-middleware');

const app = express();

// Define the target domain you want to proxy to
const target = 'https://productionUrl';

// Create the proxy middleware
const proxyMiddleware = createProxyMiddleware({
	target,
	changeOrigin: true, // Set the "Host" header to the target domain
	secure: false // Disable SSL certificate verification (only for development)
});

// Apply the CORS middleware
app.use(
	cors({
		origin: ['http://localhost:5173']
	})
);

// Apply the proxy middleware to all requests
app.use('/', proxyMiddleware);

// Start the server
app.listen(3000, () => {
	console.log('Proxy server listening on http://localhost:3000');
});

Server hosting

You can use dotenv to access environmental variables via process.env. On production we are going to want to reveal the internal pg connection string as a variable.

# excerpt from fly.toml

[env]
    PORT = "8080"
    pg_conn="postgres://super:secret@topsecret.internal:5432"

Make the necessary changes to the application logic so that the server will respect process.env.pg_conn if it’s present.

Build the typescript project with npx tsc and then deploy with fly deploy. Simple.

Client interaction with server.

Pretty simple client side code needs to generate a userName (uuid) on application load and save it to localStorage. This helps identify the user. Then just a fetch on page load and a fetch on button click.

UI Elements for Client Interaction

Another reusable component for my ever-growing Svelte Component Library h11y-slc

Putting it together

Scroll back up and click the like button! Up to 10 times (of course you can refresh the page and click it another 10 times)… If you really like it!