<Mazipan />

Create Like Button with No Cost

Available in other languages: Bahasa Indonesia

If you follow my blog, you will know that I already wrote another article about creating "Like Button" with Firebase Real Time DB. If you prefer Firebase as your source, just read the article above. If you feel bored with a mainstream platform, you might need to read this article because this is my experimental project in creating like button in very uncommon way.

The Platform

Firebase jelas saya coret dari daftar ini, karena sudah pernah menggunakannya. Firebase sangat mudah untuk digunakan dan sangat handal apalagi ditambah kemampuan untuk real time update pada semua aplikasi yang sedang diakses oleh pengguna. Kalau teman-teman memilih jalan yang mudah, saya sangat menyarankan menggunakan platform ini.

Karena saya tidak punya server dan tidak terbiasa mengurusi server maka pilihan terbaik buat saya adalah menggunakan platform Serverless. Ditambah karena saya modal dengkul dan malas keluar duit untuk hal semacam ini, maka tentu platform yang menyediakan gratisan lah yang akan jadi pilihan saya.

Sebelumnya saya sudah pernah mencoba menggunakan Heroku untuk projek graphql-pokeapi, saya tidak perlu mengurus server dan platform ini juga menyediakan versi gratis. Saya bisa memilih framework yang saya gunakan untuk membuat Rest-API sederhana. Awalnya saya memilih platform ini, sekalian karena memang ingin mencoba menggunakan fastify untuk membuat Rest-API. Sayangnya versi gratis Heroku ini sangat lambat untuk diakses, ya wajar sih sebenarnya, gratis minta cepet? Tapi kok saya gak puas ya dengan hasilnya.

Akhirnya saya mencoba platform yang lagi tenar yakni Vercel Now atau beberapa mungkin lebih kenal dengan Zeit Now. Ini pertama kali saya mencoba menggunakan Now karena memang belum ada kebutuhan dan keinginan untuk menggunakannya sebelumnya. Sekalian sajalah untuk belajar bagaimana menggunakan platform ini.

Project Initialisation

Saya tidak menggunakan CLI untuk memulai projek dengan Now ini, kalian bisa saja memulai dengan menggunakan Now CLI yang bisa lebih mudah. Saya lebih memilih memulai dengan membaca Dokumentasi platform Serverless mereka untuk memahami dan melihat kebutuhan minimal untuk membuat sebuah Rest API. Sepertinya cukup mudah dan sederhana, tapi kok seperti masih ada yang kurang ya? Ah, saya perlu melihat projek nyata seseorang membangun Rest API menggunakan platform Now. Projek pertama yang saya ingat kok ya Covid-19 API dari @mathdroid. Mencoba ubek-ubek repository yang tersedia secara terbuka tersebut, mencari tau bagaimana cara meletakkan sebuah kode, bagaimana membuat endpoint dengan parameter, dan lain sebagainya. Saya memang lebih menikmati waktu untuk ubek-ubek kode yang sudah tertata dibandingkan membaca dokumentasi 😂. Maka dari situ saya bisa memulai projek saya sendiri dengan cara berikut:

  1. Register and create an account in Vercel website.
  2. Install Now CLI for development and deployment.
  3. All endpoint is on the api/** directory
  4. The endpoint of your API is based on your directory. Same approach with Next and Nuxt. You need to manage your directory structure to create any API endpoint.
  5. Create your first API, create a file index.ts in a directory api with this simple code:
1import { NowRequest, NowResponse } from '@now/node';
2
3export default (req: NowRequest, res: NowResponse) => {
4 res.json({
5 hello: 'world!',
6 });
7};
  1. Deploy for very first time with command now, make sure you already installing Now CLI.
  2. Open your Vercel dashboard, and you can start to test your API

Saving "Like" Data

Salah satu masalah ketika menggunakan platform gratisan seperti ini memang tidak tersedianya Database yang bisa digunakan dengan mudah untuk menyimpan data. Dan sekali lagi, karena saya "tidak modal" maka saya memikirkan cara yang ngeselin untuk menyimpan data tersebut.

Karena beberapa waktu terakhir saya banyak bermain dengan API dari Github, akhirnya yang terfikir oleh saya adalah menyimpan data tersebut di sebuah Secret Gist saja. Datanya berbentuk dokumen berformat JSON sehingga mudah untuk di proses dengan kode JavaScript.

Untuk kalian yang juga ingin bermain dengan Github API, kalian bisa mencoba menggunakan pustaka Octokit Rest.js. Kalian bisa berinteraksi dengan hampir keseluruhan Github API yang tersedia untuk publik dengan interface yang lebih mudah. Kalau kalian scroll ke bagian Gists, kalian bisa menemukan interface untuk membaca dan memperbarui data yang tersedia di Gist kalian.

Hal yang perlu kalian ketahui ketika menggunakan Github API adalah terdapat Rate Limiter yang membuat kalian tidak bisa memanfaatkan API secara membabi buta.

Reading "Like" Data from Gist

Reading the data from a Gist file is quite simple, you can follow this simple code:

1const { Octokit } = require('@octokit/rest');
2
3const octokitInstance = new Octokit({
4 auth: process.env.GIST_TOKEN,
5});
6const GIST_ID = process.env.GIST_ID;
7
8const readGist = async (): Promise<any | null> => {
9 try {
10 const response = await octokitInstance.gists.get({
11 gist_id: GIST_ID,
12 });
13 return response.data;
14 } catch (e) {
15 console.error('> [GIST] - Failed read gist', e);
16 return null;
17 }
18};

Increment the "Like" Data

In every a button clicked, we need to update the counter of our "Like" data in the Gist. Here is the code for updating the counter:

1const GIST_FILENAME = process.env.GIST_FILENAME;
2
3const updateGist = async (content): Promise<any | null> => {
4 try {
5 const response = await octokitInstance.gists.update({
6 gist_id: GIST_ID,
7 files: {
8 [GIST_FILENAME]: {
9 content: JSON.stringify(content),
10 filename: GIST_FILENAME,
11 },
12 },
13 });
14 } catch (e) {
15 console.error('> [GIST] - Failed update gist', JSON.stringify(e));
16 }
17};
18
19const incrementData = async (slug): Promise<any | null> => {
20 try {
21 const existingGist = await readGist();
22 if (existingGist && existingGist.files) {
23 const content = existingGist.files[GIST_FILENAME].content;
24 if (content) {
25 const objContent = JSON.parse(content);
26 if (objContent) {
27 const currentValue = objContent[slug];
28 if (currentValue) {
29 const currentLike = parseInt(currentValue, 10) || 1;
30 const newContent = {
31 ...objContent,
32 [slug]: currentLike + 1,
33 };
34
35 await updateGist(newContent);
36 } else {
37 const newContent = {
38 ...objContent,
39 [slug]: 1,
40 };
41
42 await updateGist(newContent);
43 }
44 }
45 }
46 }
47 } catch (e) {
48 console.error('> [GIST] - Failed manipulated data', e);
49 }
50};

Endpoint

I create 3 endpoint to serve my needs in this like button, you can see the list:

1- `/api/likes` to get all Likes data in bulk mode
2- `/api/like/[slug]/get` to get like data for certain article with slug parameters
3- `/api/like/[slug]/update` to update and increment the data

To create these 3 API, you need to create directory in your project to follow Now's convention:

1- `api`
2 |- `likes`
3 |- `index.ts`
4 |- `like`
5 |- `[slug]`
6 |- `get.ts`
7 |- `update.ts`

Adding a Cache

Because Github have a rate limiter in their API, and I don't need my users see a real-time data so I decide to put a cache in my Now.json configuration.

1{
2 "headers": [
3 {
4 "source": "/api/like/(.*)/update",
5 "headers": [
6 {
7 "key": "Cache-Control",
8 "value" : "public, max-age=0, must-revalidate"
9 }
10 ]
11 },
12 {
13 "source": "/(.*)",
14 "headers": [
15 {
16 "key": "Cache-Control",
17 "value" : "public, max-age=3600"
18 },
19 {
20 "key": "Access-Control-Allow-Origin",
21 "value": "*"
22 }
23 ]
24 }
25 ]
26}

How's the result?

You can scroll to the very last of this blog, you will see a blue button with a text "Click me if you like this article". That's my simple like button powered by Now as an API provider.

If you think this article is helpful
Loading comments...

Related Post...