Revamping 1klb comments part three: displaying comments

Revamping 1klb comments

  1. Outline
  2. Database
  3. Display

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:

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:

1
2
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 like1:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// 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:

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:

1
2
3
4
5
6
{
  "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…

1
<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:

Let’s do it!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
// 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:

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. 

Comments

Leave a comment

Sorry, you need to enable javascript for commenting. That’s one of the drawbacks of having a static site blog I guess.