Serverless Comments
Sat 09 August 2025
While migrating my blog to my new domain (julian.ac!), I realized that the service I had been using for comments had not been maintained in a while, and did not support easy migration to a new domain.
I've been very impressed with the simplicity and reliability of S3 in the >10 years I've been using it to host this blog, so why not use the same setup for comments? It's not like I need to support massive write throughput or millions of comments, so a very simple system is sufficient:
- All comments for a post are stored in a single text file alongside the post itself, one JSON object per line.
- Comment management can be done entirely through a single Lambda function, in particular submission by a user and review by me.
- When displaying a post, a bit of JavaScript fetches the comment text file corresponding to the post and renders all the comments by filling in a small HTML template.
For simplicity and privacy I do not perform any authentication, users may provide their name and website if they so choose, but this is not necessary - in good 4chan tradition you can comment entirely anonymously! If you do want to prove your identity across multiple posts you can use a tripcode - basically you enter the same secret password or passphrase when submitting each comment, the server hashes it and displays the hash on the comment. For convenience the sidebar of each comment is coloured based on the tripcode, to make tripcode mismatches visually very obvious.
The result of all this is a comment system entirely without cookies, email address collection or any other form of tracking, and with no extra dependencies for serving comments - the Lambda function is only necessary when adding new comments. And it's blazingly fast too!
Implementation details
The implementation of the comment system is split into two parts, a AWS Lambda function for the "backend" and a small amount of JavaScript for the frontend.
The Lambda function currently has two parts:
Adding a comment submitted by a user:
- Accepts a POST request from the users browser with the comment data.
- Validates the comment data, e.g. to enforce size limits and check that the comment corresponds to an existing post.
- Stores the comment data in a subdirectory of the relevant post, keyed by a random comment UUID:
/path/to/post/pending/uuid.jsonl
. At this point the comment is not yet visible, and the random UUID ensures it cannot be publicly accessed. - Sends an email with the approval link to me.
Approving a comment:
- Initiated by clicking on the approval link in the email I receive, which contains post URL and comment UUID.
- Loads the comment data with the provided UUID.
- In a retry loop:
- Read the file holding all current comments, keeping the comment data and the file version ETag.
- Append the newly approved comment to data.
- Overwrite the comment file with the new data, but only if the ETag matches. If the write succeeds we are done; if not there was a concurrent modification and we retry.
- Delete the comment from the pending subdirectory.
For the current workflow the retry loop is not really necessary, but it ensures that the code still works if I want to bulk approve comments, or if I want to disable approval entirely and auto-publish comments.
Tags: blog, comments, aws, lambda, serverless
italics | surround text with *asterisks* |
bold | surround text with **two asterisks** |
hyperlink | [hyperlink](https://example.com)or just a bare URL |
code | surround text with `backticks` |
surround text with ~~two tilde characters~~ | |
quote | prefix with > |
Loading comments...