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:
- visit https://dev.to/settings/account
- in the "DEV API Keys" section create a new key by adding a description and clicking on "Generate API Key"
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:
Use the client
After generating the client, I just had to write some code which:
- retrieves the number of reactions and comments for the article
- 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: ...
---
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")
}
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
where wrapper.sh is a bash script which calls the program:
#!/bin/bash
source .env
./update-article >> /home/napicella/ua.log 2>&1
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
Top comments (11)
As soon as I saw the title I knew it was Tom Scott 😁
Steve Mould did a follow up too
It just clicked to me what this is doing. Really trippy. Coding is awesome. 😄
Cool idea :D
:)
This is so incredibly cool! I think my favorite part is that on Twitter, the preview updates with the new title too: twitter.com/napicellatwit/status/1...
So... It has been more than a couple minutes now... nothing has happened lol!
I noticed that comments update immediately, reactions instead might take some time. Anecdotally (i.e. looking at the log), in some cases it took up to 15 mins :)
Oh, cool. I see the updated title now.
This is great!
I did a post a while back covering how I might have done the Youtube way too
dev.to/deepfrieddev/updating-a-you...
Interesting idea! Congrats!
thanks XD