Lorenzo's Blog About me

How to make a decentralized like button

ATTENTION!

This article is 3 years old, it may be outdated or not relevant anymore! Comments and reactions might have been lost.

On  - Reading Time: 5 Minutes,

All static websites meet a hard limit when a comment form, “like” buttons, or any interactive content is required. These extra components force people to build an API, host a server, a database or pay for any SaaS product that does that… we could write a blog post about that, but it would be too easy! 🤣 Instead in this article, I wanted to challenge myself a little more and explore how to make a decentralized like button: We will try how it is to not be hosting any server or database, and instead use a p2p networks and a decentralized database to count the likes. 👍

Decentralized vs Centralized
Decentralized vs Centralized

To achieve that, I have been considering just one decentralized technology that I am very familiar with: the Interplanetary File System. This will allow us to make a simple HTML widget that works on any modern browser and gets data to show from a prominent and established p2p network.

As always, you can access the source code on GitLab. There is a website to see the results here: https://dlike.qm64.tech/ and set up

A little bit about IPFS and OrbitDB

The Interplanetary File System (AKA, IPFS) is a peer-to-peer hypermedia protocol. IPFS allows us to send and receive data using some concepts that are similar to BitTorrent. This video from 2018 explains in short what IPFS is.

OrbitDB is one of the few databases that use IPFS API to replicate and distribute content across the peers. It supports JavaScript, it runs in any modern browser, and it looks [documented enough].

This means that the data generated by the widget (the clicks) will not be stored in a central SQL/NoSQL database, but it will be spread across all the page viewers. Using OrbitDB and IPFS allows us to build a like button that doesn’t require a Blockchain nor a central server.

This page is all briefly about how to use IPFS and OrbitDB to create a decentralized application (dApp) as an HTML widget for our websites, to record likes/votes from users.

The Goals

I will consider Qm64 as our use case for this widget: this blog is generated with Hugo, and adding a Like button would require to use an external service (ex: Facebook or Disqus) or to set up my own centralized server. Instead, it needs an HTML widget that can be copy-pasted and customized a little, similar to Disqus or Facebook or other social buttons.

In short the goals of this project are:

  • Make a decentralised HTML widget (dApp? dComponent?)
  • It should be a like button
  • It should store data on IPFS/OrbitDB
  • This widget should be able to show changes in real time
  • It should automagically connect to the network
  • The user/website owner should not care about too much tech details

The process

The first step is to set up an HTML widget in Preact using Webpack, that is the core part of the UI, but t is not the most interesting one! We will not cover this part.

To show something to the UI, we need to connect to the network: load IPFS and OrbitDB instances to listen to events. To do so, we are using something similar to the following lines:

const IPFS = require('ipfs')
const OrbitDB = require('orbit-db')

IPFS.create()
 .then(OrbitDB.createInstance(ipfs))
 .then((orbit) => {
   // some magic here
 })

OrbitDB supports different types of databases. We are developing a simple like button; therefore, a Counter database will work just fine. 😛 On top of that, as the users are going to click it without any authentication, we need to allow anybody to make changes. The last thing required is to find a name for the database. This is done with the following lines:

const DATABASE_OPTIONS = {
  // Give write access to everyone
  accessController: {
    write: ['*']
  }
}

// Creating the database as a counter with the custom options
const db = orbit.counter("qm64-tech", DATABASE_OPTIONS)

This will create a new database that will be identified with a unique ID. We must note this ID: We need to use it to allow browsers/peers to find each other and fetch the data! 📡📡📡 For convenience, I refer to this ID as the dbkey:

const dbkey = db.address.toString()
console.log(dbkey)
// it will log something like `/orbitdb/Qmd8....Q3EVSU/qm64-tech`

The last thing we need to know is to try and learn how to increase the counter and check the latest value:

// Get latest value
console.log(db.value) // will log 0

// Increase the counter
db.inc()

console.log(db.value) // will log 1

That seems simple right? YES! Too simple! 😅 But that is the beauty of it: the database synchronization between peers is done in the background using IPFS and OrbitDB instances. 🚀 We can listen to specific events to know when the data s replicated too, and that is useful to build react components and update the state.

What is left is to glue all this together in a React Component, then use it in an HTML widget that can be included everywhere. Since this page is about OrbitDB, I created a page that generates an HTML code that can be copy-pasted and added into the pages. The page also shows the button and allows you to insert an existing dbkey so that you can test the widget from other devices too.

Try it out! Open on your browser dlike.qm64.tech, create a new widget, and keep the page open. Then take another device (your phone or iPad, for example) and go to the same page, but under Existing Widget, insert the dbkey shown on the other browser! It might take a few seconds or tries, but it should work! 😎

dlike setup example

dlike setup example

In this gif, you can see me setting it up. Then I am copy-pasting the same value on my iPhone and pressing the like button there while keeping the original page open. 🎉🎉 It works 🎉🎉

Conclusions

This project is quite exciting. I was expecting it to be way more complicated. Still, technically speaking, a lot of problems have been solved already by different libraries and tools developed as part of IPFS and OrbitDB.

Sadly the final minimized build of the whole javascript widget is big! (1.83 MB) A lot of these implementations are required to have a fully decentralized and peer-to-peer network. Some of them are related to cryptography; some others are just a mechanism to make it work. This would be solved if browsers would implement an IPFS node within their binaries, but only a few are doing so… and not the popular ones!

Even if all went good on the first setup, the project is still young and lacking some features. One of these is the lack of a daemon to replicate data. This is important because of content distribution in the network: If nobody currently online has a copy of the database, nobody will be able to see the button! I have been considering rewriting it using Textile’s ThreadsDB as it has an implementation in Go and a hub to replicate content.

This means that we don’t need a central database or an API, but we still need a solution to replicate the content in the network to ensure that it is alive.

Are we ready yet for dApps?

There are a lot of things to consider. The like button is just an example, and this conclusion might is based on a side-project that lasted merely 3 months.

So far, what we conclude is that implementing a decentralized app (dApps) just with IPFS and OrbitDB but without using a blockchain is not enough! Why? In the current setup, anybody could create a simple script to sends likes on pages. Using Ethereum would force users to pay a small fee but will reduce the cost of such kind of attack on the website!

On top of that, as said before: it is required some sort of persistence to respect an SLA. Websites with a huge amount of users could afford to have dApps (or components), but to have replicas is also required on Web 2.0 services to ensure an SLA.

Was this a success?

Concluding, the libraries and development tools are already available. We should all start developing more using this tech as it might be really the future! I believe that this experiment is a partial success. Even if we reached our goals, the infrastructure required some tweaking: the dev tools and libs are available, but we need major browsers to implement changes for major dapps to work without any workarounds. I would say that this like button works, but it is limited by the current state of development.

If you want to explore more you can read the following articles: