This article has 86 reactions and 11 comments

napicella

Nicola Apicella

Posted on May 13, 2020

This article has 86 reactions and 11 comments

Tom Scott made a video recently where the title contains the number of views that the video has. The title is kept up to date with the number of views by a program which calls the youtube API. It's a great video if you haven't seen it yet.

Anyway, I really liked that idea and wanted to try it with this dev.to article. The article is different from Tom's video in two ways: the title of the article contains the number of reactions and comments instead of the views. The second thing is the topic. Tom talks about the invention of the APIs. What I want to talk about is how to use APIs to call the dev.to service.

The title of the article won't exactly match the number of comments and reactions because the program that updates the title only runs every minute. Also, I suspect the dev.to api is eventual consistent, which means the number of reactions and comments will be updated at some point, but it does not happen in real time. If, when you are reading the article, the title matches or is close to the number of reactions or comments, it means that the code is still working; but it could stop working any time. The dev.to api I am using is in beta and could change or being dismissed in the future.

In the remainder of the article I am going to talk about the steps I took to use the dev.to api.

OpenAPI specification

The first step to build a program which interacts with a service is to discover its api.

OpenAPI specification defines a standard which allows both developers and machines to discover and understand the capabilities of a service. It serves as documentation and as a contract.

An API defined via an OpenApi document is machine friendly, which means we can build programs that interpret that document and do something useful with it. For example, the oapi-codegen is able to read the document and generate a golang http client to interact with the api. It can also generate a stub service which can be used both as starter or as a mock for your tests.

The other benefit of OpenAPI is that is also human friendly. It can be used as documentation, hosted on a server and browsed via a browser. This is what dev.to uses and anybody can read and download its OpenApi spec here.

AuthN and AuthZ

The second step to build the program is getting valid credentials.

Services need a way to know that you are who you say you are (authentication). Once they have established that, they might also need to verify that you can carry on the action you want to perform (authorization).
In the case of dev.to, the api key is used for authN and authZ. An api key is a pseudo random service generated string which is associated with your account. To obtain one for dev.to:

Generate the client

Once we have the OpenAPI document and the api key, we are ready to make good use of them.

I have used the oapi-codegen tool to generate a golang client to interact with the api. The process was quick. I downloaded the OpenAPI document for dev.to and run the oapi-codegen, and that was it, no errors or warnings. Sometimes you need to tweak a bit the document or the code generator to make it work, but not in this case. The result was a golang struct with the following methods:

client

Use the client

After generating the client, I just had to write some code which:

  1. retrieves the number of reactions and comments for the article
  2. updates the title if the number of reactions or comments changed

The second step require a little bit more work than the first one.
Since, I use front matter when writing on dev.to, updating the title means updating the title field in markdown:

---
title:This article has x reactions and y comments
published: true
description: ...
tags: ...
---
Enter fullscreen mode Exit fullscreen mode

The gist of the program is here:

func run(ctx context.Context, env *programEnv, client *ClientWithResponses) {
    r, e := getArticleActivity(ctx, client, env.ArticleID)
    if e != nil {
        log.Fatal(e)
    }
    log.Debugf("Article reactions: %[1]d - Article comments: %[2]d",
        r.ReactionsCount,
        r.CommentsCount)

    newTitle := generateNewTitle(r.ReactionsCount, r.CommentsCount)
    fmEditor, e := newFrontMatterEditor(r.BodyMarkdown)
    if e != nil {
        log.Fatal(e)
    }
    if fmEditor.title == newTitle {
        log.Debug("Article title is already up to date with the number of reactions and comments")
        return
    }

    log.Debug("Updating the title of the article")
    fmEditor.updateTitle(newTitle)
    e = saveArticle(ctx, client, env.ArticleID, fmEditor.markdown)
    if e != nil {
        log.Fatal(e)
    }
    log.Info("Article with the new title has been saved")
}
Enter fullscreen mode Exit fullscreen mode

You can find the rest of the source code is on github.

Profit

Lastly, I create a cron job which runs the program every minute:

SHELL=/bin/bash
* * * * * /home/napicella/wrapper.sh
Enter fullscreen mode Exit fullscreen mode

where wrapper.sh is a bash script which calls the program:

#!/bin/bash

source .env
./update-article >> /home/napicella/ua.log 2>&1 
Enter fullscreen mode Exit fullscreen mode

Dev.to documentation does not mention any service quota for the api. I did not get throttled so far, so I guess it's a reasonable rate for the service.

Conclusion

I want to conclude by thanking Tom Scott for the idea. If you haven't seen it, this is the link to the video.


Follow me on Twitter to get new posts in your feed.
Credit for the cover image to GraphicMama-team

💖 💪 🙅 🚩
napicella
Nicola Apicella

Posted on May 13, 2020

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related