{"id":17942930,"url":"https://github.com/nickcellino/nbb-comments","last_synced_at":"2025-03-24T14:32:57.771Z","repository":{"id":58481816,"uuid":"532063105","full_name":"NickCellino/nbb-comments","owner":"NickCellino","description":"A service for adding basic comment functionality to any blog post/webpage.","archived":false,"fork":false,"pushed_at":"2022-09-03T19:39:11.000Z","size":302,"stargazers_count":16,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-03-19T03:56:36.265Z","etag":null,"topics":["clojurescript","dynamodb","lambda","nbb","serverless"],"latest_commit_sha":null,"homepage":"https://nickcellino.com/blog/2022-09-03-nbb-comments.html","language":"Clojure","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/NickCellino.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2022-09-02T20:01:36.000Z","updated_at":"2024-10-06T00:19:24.000Z","dependencies_parsed_at":"2023-01-17T19:47:33.171Z","dependency_job_id":null,"html_url":"https://github.com/NickCellino/nbb-comments","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NickCellino%2Fnbb-comments","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NickCellino%2Fnbb-comments/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NickCellino%2Fnbb-comments/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NickCellino%2Fnbb-comments/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/NickCellino","download_url":"https://codeload.github.com/NickCellino/nbb-comments/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245289831,"owners_count":20591140,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["clojurescript","dynamodb","lambda","nbb","serverless"],"created_at":"2024-10-29T03:23:47.293Z","updated_at":"2025-03-24T14:32:57.461Z","avatar_url":"https://github.com/NickCellino.png","language":"Clojure","funding_links":[],"categories":[],"sub_categories":[],"readme":"# nbb-comments\n\nA service for adding basic comment functionality to any blog post/webpage. Visitors to your site can leave comments and view others' comments.\n\n- Built with [nbb](https://github.com/babashka/nbb)\n- Deployed on AWS Lambda using [Serverless Framework](https://www.serverless.com/)\n- Uses DynamoDB for storage\n\nYou can:\n- [Deploy it](#using-it-on-your-own-site) to your own AWS account to use it on your own site\n- [Run it locally](#running-server-locally) to poke around and see how it works\n- [See it in action](https://nickcellino.com/blog/2022-09-03-nbb-comments.html) (scroll to the bottom of the linked page)\n\n## Why\n\nI wanted to allow people visiting [my personal blog](https://nickcellino.com) to be able to leave comments without creating any sort of account/login. I used Google reCAPTCHA v3 to prevent bot abuse.\n\n## Overview\n\n`nbb-comments` uses an AWS Lambda function (backed by DynamoDB) to serve HTTP requests to:\n- add a new comment for a certain blog post\n- list existing comments for a certain blog post\n- retrieve the HTML to render the comments form\n\nIt uses [htmx](https://htmx.org/) to handle interactions with the server and make dynamic updates to the DOM without reloading the page.\n\nOnce you deploy your backend, adding it to any webpage is as simple as:\n```html\n\u003c!-- add this to \u003chead\u003e --\u003e\n\u003cscript src=\"https://unpkg.com/htmx.org@1.8.0\" async\u003e\u003c/script\u003e\n\n\u003c!-- add this somewhere in your \u003cbody\u003e --\u003e\n\u003ch2\u003eLeave a comment\u003c/h2\u003e\n\u003cform id=\"comment-form\" hx-get=\"https://\u003cyour-deployed-lambda-url\u003e/comments-form?post-id=example-post-id\" hx-trigger=\"load\"\u003e\u003c/form\u003e\n\n\u003ch2\u003eComments\u003c/h2\u003e\n\u003cdiv id=\"comments-list\" hx-get=\"https://\u003cyour-deployed-lambda-url\u003e/comments?post-id=example-post-id\" hx-swap\"innerHTML\" hx-trigger=\"load\"\u003e\u003c/div\u003e\n```\n\n## Running server locally\n\n##### Prerequisites\n- Install [babashka](https://babashka.org/)\n- Install [node/npm](https://nodejs.org/en/download/)\n\nFirst, install node dependencies:\n```bash\nnpm install\n```\n\nThen, to run the server locally, run:\n```bash\nbb dev-server\n```\n\nThen visit `http://localhost:3000` and you can see it in action!\n\n## Using it on your own site\n\nTo use this for your own blog/site, you will need to:\n\n1. [Deploy your own instance](#deploy-your-own-instance)\n2. [Hook it up to your frontend](#hook-it-up-to-your-frontend)\n\n### Deploy your own instance\n\n#### Prerequisites\n- [Setup your AWS credentials](https://www.serverless.com/framework/docs/providers/aws/guide/credentials) so that you can deploy using Serverless Framework \n- [Register a Google reCAPTCHA v3 key](https://www.google.com/recaptcha/admin/create)\n- Install [node/npm](https://nodejs.org/en/download/)\n\n1. Create an `.env` with the following contents:\n\n    ```\n    RECAPTCHA_SECRET=\"\u003cyour-recaptcha-secret-here\u003e\"\n    RECAPTCHA_SITEKEY=\"\u003cyour-recaptcha-sitekey-here\u003e\"\n    ALLOWED_ORIGIN_URL=\"\u003cyour-frontend-url\u003e\" # for example \"https://nickcellino.com\"\n    ```\n\n2. Run `npm install` in the root of this project.\n\n3. Run `npx serverless deploy`. If everything worked correctly, this should print out something like:\n    ```bash\n    ➜  npx serverless deploy\n\n    Deploying comments-api to stage dev (us-east-1)\n\n    ✔ Service deployed to stack comments-api-dev (84s)\n\n    endpoint: ANY - https://s390h072qf.execute-api.us-east-1.amazonaws.com/{proxy+}\n    functions:\n      comments-api: comments-api-dev-comments-api (65 MB)\n\n    Monitor all your API routes with Serverless Console: run \"serverless --console\"\n    ```\n\nTake note of the endpoint url you get back. In this example, it is `https://s390h072qf.execute-api.us-east-1.amazonaws.com`\n\nIf you can see that, your backend is all set!\n\n### Hook it up to your frontend\n\n1. Load `htmx` script somewhere in the `\u003chead\u003e` of your HTML like so:\n    ```html\n    \u003chead\u003e\n    ...\n    \u003cscript src=\"https://unpkg.com/htmx.org@1.8.0\" async\u003e\u003c/script\u003e\n    ...\n    \u003c/head\u003e\n    ```\n    This is used to dynamically fetch the comments form and comments from the backend.\n\n2. Add the comments form somewhere on your page like so (replacing *your-backend-url* with the proper value for your backend):\n    ```html\n    \u003cform\n      id=\"comment-form\"\n      hx-get=\"\u003cyour-backend-url\u003e/comments-form?post-id=example-post-id\"\n      hx-trigger=\"load\"\u003e\n    \u003c/form\u003e\n    ```\n    \n    For this step and the next, you should specify a value for the post-id query parameter that is specific to whatever page you are adding this functionality to. If you add this to a different page, you would use a different post-id in order to have a separate comment thread per-page.\n\n3. Add the comments list (where the comemnts will actually be displayed) somewhere on your page like so (replacing *your-backend-url* with the proper value for your backend):\n    ```html\n    \u003cdiv\n      id=\"comments-list\"\n      hx-get=\"\u003cyour-backend-url\u003e/comments?post-id=example-post-id\"\n      hx-swap\"innerHTML\"\n      hx-trigger=\"load\"\u003e\n    \u003c/div\u003e\n    ```\n\nOnce you have done that, you are all set to receive brilliant insights from random strangers on the internet!\n\nYou can find a minimal working example of this [here](./src/dev/index.html).\n\n## Styling\n\nThe HTML snippets above will load HTML from the server and render it onto the page.\nYou will need to know what that rendered HTML looks like in order to style it.\n\nYou can also run [the dev server](#running-server-locally) to play around with the styling.\n\n#### Rendered \"add comment\" form example\n\n```html\n\u003cform\n  id=\"comment-form\"\n  hx-post=\"http://localhost:3000/comments\"\n  hx-swap=\"afterbegin\"\n  hx-target=\"#comments-list\"\n  hx-trigger=\"submit\"\n  hx-swap-oob=\"true\"\u003e\n\n  \u003cinput type=\"hidden\" name=\"post-id\" value=\"example-post-id\"\u003e\n  \u003clabel for=\"author\"\u003eName (optional)\u003c/label\u003e\n  \u003cinput type=\"text\" name=\"author\" id=\"author-input\" hx-swap-oob=\"true\"\u003e\n  \u003clabel for=\"message\"\u003eComment\u003c/label\u003e\n  \u003ctextarea name=\"message\" required=\"true\" rows=\"5\" id=\"message-input\" hx-swap-oob=\"true\"\u003e\u003c/textarea\u003e\n\n  \u003cscript\u003e\n    function announce(token) {\n      const event = new Event('recaptcha-verified');\n      const elem = document.querySelector('#comment-form');\n      elem.dispatchEvent(event);\n    }\n  \u003c/script\u003e\n  \u003cscript src=\"https://www.google.com/recaptcha/api.js\"\u003e\u003c/script\u003e\n\n  \u003cbutton type=\"submit\"\u003eSubmit\u003c/button\u003e\n\n\u003c/form\u003e\n```\n\n#### Rendered comment list example\n\n```html\n\u003cdiv\n  id=\"comments-list\"\n  hx-get=\"http://localhost:3000/comments?post-id=example-post-id\"\n  hx-swap=\"innerhtml\"\n  hx-trigger=\"load\"\u003e\n \n  \u003cdiv class=\"comment\"\u003e\n    \u003cp class=\"name\"\u003e\n      \u003cstrong\u003eAnonymous\u003c/strong\u003e said...\n    \u003c/p\u003e\n    \u003cp class=\"message\"\u003eHi there\u003c/p\u003e\n    \u003cp class=\"datetime\"\u003e20:22, September 2, 2022\u003c/p\u003e\n  \u003c/div\u003e\n\n  \u003cdiv class=\"comment\"\u003e\n    \u003cp class=\"name\"\u003e\n      \u003cstrong\u003eNick\u003c/strong\u003e said...\n    \u003c/p\u003e\n    \u003cp class=\"message\"\u003eGreat post, Nick!\u003c/p\u003e\n    \u003cp class=\"datetime\"\u003e20:21, September 2, 2022\u003c/p\u003e\n  \u003c/div\u003e\n\n\u003c/div\u003e\n```\n\n#### Styling example\n\nThere is a very, very basic example of styling in [the example HTML](./src/dev/index.html).\n\n```html\n\u003cstyle\u003e\n  .comment {\n    border-bottom: 1px solid;\n  }\n\n  input, textarea {\n    width: 100%;\n    margin-bottom: 5px;\n  }\n\n  button {\n    margin-top: 5px;\n  }\n\u003c/style\u003e\n```\n\nThat will result in this magnificence:\n\n![example.png](./example.png)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnickcellino%2Fnbb-comments","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnickcellino%2Fnbb-comments","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnickcellino%2Fnbb-comments/lists"}