Revamping 1klb comments part three: displaying comments

Published
25 May 2021
Tagged
Part of a series: Revamping 1klb comments

In the previous two posts on this subject I showed off the big picture overview of how this whole system was going to work, and showed you how I set up my database in Fauna to deal with those comments. Assuming that all went according to plan, let's explore how we actually go from bits in a database, to, yknow, comments on the website.

Two ways of talking to Fauna

I've mentioned previously that there's two ways of talking to the Fauna database:

  • via Fauna Query Language, Fauna's proprietary language for interacting with the database. The javascript driver for this language is smooth like butter, but it is a few hundred kb.
  • via GraphQL, a more widely-used web-based query API. GraphQL queries are more difficult to write, but don't require their own library to submit. This makes them ideal for places where we're trying to keep things light and breezy.

We're going to get stuck into the FQL driver for Fauna when we design the admin panel, don't worry. But for the web-based commenting system, we're going to fetch the data entirely using GraphQL. This will make individual page loads quicker on the blog pages themselves, which I figure is where it counts.

A proof of concept

Note that for the following section I assume you have some kind of static website blog that you can include javascript on.

Before we can start building anything, we'll need an API key. In our previous step we created a role called ReadComments. As you may guess, that's the role that our website will take - all it needs to do is read individual comments so it can display them on the webpage. We'll need to grab an API key for this role and embed it into our javascript, for us to use when we make our queries. It doesn't matter if other people know this API key - it just means they can read the comments on the website.

To grab that API key:

  1. Log into your Fauna account and open the database you're working in.
  2. Click Security > + NEW KEY.
  3. Select the correct role, fill in the text fields as you see fit, and click save.
  4. Copy the provided API key.

Now create a javascript file that will be loaded into your blog page. Right now we're just going to set up that API key:

let FAUNA_API_KEY = "your-api-key-here"
let FAUNA_GRAPHQL_ENDPOINT = "https://graphql.fauna.com/graphql"

Now we've got an API key, we can try getting in touch with the Fauna website. This is what a GraphQL query looks like[1]:

// Specify the post slug. This is the unique text string that identifies a given blog post
let post_slug = window.location.pathname.replace("^/posts/", "")


// Make GraphQL query
let query = {
  operationName: "PostComments",
  query: `query PostComments {commentsByPostSlug(post_slug:"${post_slug}") { data { body submitter created_at} }}`
}

// Populate options, including header, method, and body
let fetchOptions = {
  headers: { Authorization: `Bearer ${FAUNA_API_KEY}` },
  method: "POST",
  body: JSON.stringify(query)
}

// Perform the operation
fetch(
  FAUNA_GRAPHQL_ENDPOINT,
  fetchOptions
)
  .then(r => r.json())
  .then(payload => console.log(payload))

So what did we do here? In three easy steps, we:

  1. Determined the "post slug" for this blog post. This assumes that all your blog posts sit in the directory /posts/ on your website.
  2. Created the body of a GraphQL query. We called this query "PostComments" (this can be any name you want it to be), and set it up to download the comments for the current post we're looking at.
  3. Populated the options for the GraphQL query. This includes the authentication (via API key) and HTTP method as well as the body we've just created.
  4. Ran the fetch, parsed the result, and spat it out to the console.

Some notes at this stage:

  • We're not doing any checking here. If this runs on every page, we should probably check that the page is actually a blog post before running queries against the database.
  • We're not doing anything with the result. We're just outputting it to the console. So don't expect anything fancy to happen.
  • In fact, we shouldn't expect anything fancy to happen at all, as we don't have any comments in our database.

Let's test it

To test this, we'll need to go to Fauna and make a test comment. Once everything is set up, you should be able to submit a comment via the website, but for now we'll just make one through the back-end.

To do this properly, you'll first need to work out the slug for a page you can test this on. Once you have that, go to your Fauna dashboard and create a new Comment record that looks like this:

{
  "post_slug": <your post slug here>,
  "body": "This is a sample comment",
  "submitter": "Me",
  "created_at": Now()
}

This should mean that Fauna will retrieve this comment when it hits the GraphQL endpoint we made above. Try refreshing your page (with the javascript loaded into it) and see if the comment appears in your console. If it does, you know it's working!

Populating a comment section

So we're fetching the comment, and it should be coming through via our javascript fetch. Our next goal is to actually turn raw JSON into a nice set of comments.

First, let's make sure there's somewhere on the blog page where comments go. Perhaps...

<div class="comments-section"><noscript>You need to turn javascript on to see comments here.</noscript></div>

Now how do we populate this section? Well, we'll need to do the following:

  • Get the "slug" (URL identifier) for this post
  • Hit that GraphQL endpoint we set up above, with the relevant options
  • And then populate the page with those comments

Let's do it!

// Set up useful values
let commentsSection = document.querySelector(".comments-section")
let FAUNA_GRAPHQL_ENDPOINT = "https://graphql.fauna.com/graphql"
let FAUNA_API_KEY = "add your API key here"

// Post slug = URL path
let post_slug = window.location.pathname


// Assemble graphql query and headers
let query = {
  operationName: "PostComments",
  query: `query PostComments {commentsByPostSlug(post_slug:"${post_slug}") { data { body submitter created_at} }}`
}

let fetchOptions = {  
  headers: { Authorization: `Bearer ${FAUNA_API_KEY}` },
  method: "POST",
  body: JSON.stringify(query)
}

// Perform the fetch operation
fetch(
  FAUNA_GRAPHQL_ENDPOINT,
  fetchOptions
)
  .then(r => r.json())
  .then(payload => {
    // Payload should be an array of comments, each of which
    // contains `post_slug`, `body`, `submitter`, `created_at`

    payload.forEach(comment => {
      // Make a date in the form dd/mm/yyyy
      let dateString =
        [
          comment.created_at.getDate(),
          comment.created_at.getMonth() + 1,
          comment.created_at.getFullYear()
        ]
        .map(e => e.toString().padStart(2, "0"))
        .join("/")

      // And let's ensure we escape html in user-supplied values
      let submitter = new Option(comment.submitter).innerHTML
      let body = new Option(comment.body).innerHTML

      // Build the comment. Use Option().innerHTML to escape html in the comment text
      let thisComment = document.createElement("div")
      thisComment.classList.add("comment")
      thisComment.innerHTML = `
        <div class="comment-metadata">
          <div class="comment-submitter">${submitter}</div>
          <div class="comment-date">${dateString}</div>
        </div>
        <div class="comment-body">${body}</div>`

      commentsSection.appendChild(thisComment)
    })
  })   

You may need to change the relevant CSS selectors to fit in with your own page. But if everything lines up, you should find yourself with your sample comment appearing on your page

To conclude

At this stage:

  • We can fetch comments from our database within our app
  • We can programmatically display them on our webpage!

Feels like a lot of work for two bullet points, right? That's OK - this will be a great way to test things as we move forward. In the next post, I'll go over setting up commenting and moderation. Now we've done this work, actually implementing commenting isn't too difficult, but moderation is going to be a fun little journey.


  1. Full disclosure: I know next to nothing about running a good GraphQL query. I've picked most of this up by trial and error, correcting this code over and over again until it runs without causing any issues. ↩︎