Membuat Realtime Chart dengan Express, ChartJs, MongoDB dan Socket IO

Ahmad Oriza 19 Februari 2019

Membuat Realtime Chart dengan Express, ChartJs, MongoDB dan Socket IO

Membuat chart mungkin sudah biasa, bagaimana jika kita buat chart terasa hidup dengan data real-time? hal ini dapat kita capai dengan menerapkan teknologi push dengan websocket.

Pendahuluan

Target pembaca tutorial ini adalah teman-teman yang baru belajar javascript, Node dan Express. Sedikit paham tentang MongoDB. Tujuan tutorial ini untuk memberikan gambaran bagaimana cara penerapan ChartJs dengan sentuhan SocketIO.

SocketIO merupakan tool yang dibangun diatas WebSocket API, dia menyediakan API client dan server yang dapat kita gunakan untuk menerapkan push dalam aplikasi. Sedangkan ChartJs merupakan library chart yang cukup banyak digunakan karena penerapannya yang simple.

Persiapan

Aplikasi yang akan kita buat adalah aplikasi voting sederhana. Tugas dari aplikasi ini adalah menerima data voting yang ditransfer via HTTP Post, kemudian mengakumulasinya dan menampilkan kembali hasil voting dalam bentuk chart. Setiap ada update data dari HTTP post maka chart ini secara real-time akan berubah bentuk dengan sedikit animasi sederhana. Oh ya, kita tidak bikin html form disini, kita akan insert data dari Postman saja (teman-teman bisa gunakan HTTP tool lain), karena pembuatan form tidak terlalu penting dalam konteks tutorial ini.

Membuat Aplikasi

Struktur folder dari aplikasi kita akan terlihat seperti ini :

  1. app.js, menjadi main file dan yang menyimpan semua kode server side kita
  2. folder public, menjadi tempat dimana kode template client side disimpan dan diakses publik.
  3. package.json, tempat simpan daftar package yang kita butuhkan (npm)
  4. node_modules, folder dimana package terinstall

Install Dependency

Oke, disini kita akan tentukan kebutuhan dalam aplikasi, silahkan buat file package.json berisi data seperti ini :

{
name: "RealtimeChart",
main: "app.js",
dependencies: {
	express: "latest",
	mongoose: "latest",
	body-parser: "latest",
	socket.io: "latest"
	}
}

Bisa dilihat, kita membutuhkan dependencies sebagai berikut :

  1. Express sebagai framework
  2. Mongoose (mongodb wrapper) sebagai object modelling, penggunaannya seperti ORM. Untuk memudahkan kita dalam query yang lebih simple
  3. Body-parser, merupakan middleware untuk Express, berguna sebagai parser data dari HTTP Body. Library ini akan mengubah data ke javascript object, agar dapat kita panggil dalam aplikasi.
  4. Socket.io, wrapper websocket API untuk penggunaan websocket yang lebih sederhana.

Baiklah, silahkan jalankan npm install. Jika berhasil, folder node_modules beserta isinya akan terbentuk.

Inisialisasi

Selanjutnya mari kita bikin file app.js. Kita akan melakukan inisialisasi dari semua library yang kita butuhkan seperti berikut ini :

// Dependency
var express  = require('express');
var mongoose = require('mongoose');
var bodyParser = require('body-parser');
var app      = express();
var server   = require('http').Server(app);
var io       = require('socket.io')(server);

// Config
app.use(bodyParser.urlencoded({ extended: true }));
app.use(function(req,res,next){
	req.io = io;
	next();
});

// Static files path
app.use(express.static(__dirname + '/public'));
...

Pada baris 2-7 kita bungkus library yang sudah kita unduh dan memasukannya dalam variabel. Baris ke 5 terlihat kita assign framework express, pada baris ke 6 terlihat kita define dependency HTTP server.

Masuk kebagian config, pada baris 10 kita jalankan method app.use dari Express kemudian memasukan function callback dari library body-parser. Hal ini berfungsi memerintahkan Express untuk jalankan parsing body ketika route dipanggil. Kemudian pada baris ke 11 adalah perintah untuk memerintahkan Express jika request adalah socket io kemudian lanjutkan program, hal ini berguna agar library socket io bisa kita gunakan dalam HTTP Route di Express.

Nah, pada baris 17 merupakan perintah pendefinisian dimana folder file static berada. Disitu kita menggunakan express.static yang merupakan fungsi built-in middleware di Express.

Database

Pada program ini kita hanya butuh collection sederhana, berikut ini kode terusan dari bagian "Inisialisasi" diatas. Silahkan tambahkan kode berikut :

// Connect to DB
mongoose.connect("mongodb://127.0.0.1:27017/realtime_chart");

var schema = mongoose.Schema({name: String});
var Vote = mongoose.model('Vote', schema);

...

Note: Jangan lupa untuk menghidupkan dulu service MongoDB untuk menghindari error. Jika teman-teman baru di MongoDB silahkan baca dasar-dasar MongoDB, mungkin artikel ini bisa jd referensi https://www.codepolitan.com/tutorial/pdkt-dengan-mongodb

Untuk database saya menggunakan db dengan nama "realtime_chart", teman-teman bebas menggunakan nama db lain. Pada baris ke 4 kita define collection berisi hanya satu field yaitu "name" berjenis "string". Kemudian pada baris ke 5 adalah kode untuk assign mongoose model dengan schema yang sudah kita buat ke dalam variabel Vote. Sebagai catatan, pada kode berikutnya operasi database akan menggunakan model "Vote".

Mendefinisikan Route, Query, dan Broadcast

Oke, masih terusan kode sebelumnya, sekarang masukan kode berikut ini.

// Render homepage.
app.get('/', function(req, res) {
	res.sendfile('index.html');
});

// Route for voting
app.post('/vote', function(req, res) {
	var field = [{name: req.body.name}];

	var newVote = new Vote(field[0]);
	
	newVote.save(function(err, data) {
		console.log('Saved');
	});

	Vote.aggregate(
		
		[{ "$group": {
			"_id": "$name",
			"total_vote": { "$sum": 1 }
		}}],

		function(err, results) {
			if (err) throw err;
			console.log(results);
			req.io.sockets.emit('vote', results);
		}
		);

	res.send({'message': 'Successfully added.'});
});

app.get('/data', function(req, res) {
	Vote.find().exec(function(err, msgs) {
		res.json(msgs);
	});
});

...

Pada baris ke 2 kita define homepage. Maksud dari kodenya, ketika url yang diakses adalah (/) jalankan res.sendfile ke file index.html. Kode tersebut akan parsing file template index.html yang terletak pada folder public (kita akan membuat file tersebut pada bagian selanjutnya).

Pada baris ke 7 kita define rute dengan segment vote. Ketika url (/vote) diakses maka jalankan perintah pada callback function. Di dalam fungsi tersebut pada baris ke 8 kita ambil data http body bernama "name" dan memasukannya dalam variabel field.

Nah pada baris ke 10-14 kita membuat document/object baru menggunakan model "Vote". Kemudian pada baris 16-30 adalah kode untuk mengambil data total dari voting. Kita disitu menggunakan method "aggregate", berguna untuk menghitung total document berdasarkan field tertentu.

Oke, coba perhatikan baris 26. Disitu kita menjalankan perintah socket emit (broadcast/push) data ke semua client yang terhubung dengan room "vote". Room dalam konsep websocket berguna untuk mengkategorisasikan client yang terhubung dengan server.

Terakhir, pada baris 33-37 merupakan kode sederhana untuk menampilkan data-data voting dalam bentuk JSON ketika url (/data) dipanggil. Ini kita gunakan untuk kepentingan pengecekan data saja.

Menambahkan Event Socket IO

Oke, ini merupakan bagian akhir dari kode di sisi server. Silahkan tambahkan kode berikut ini :

/*
Socket.io Setting
*/

io.on('connection', function (socket) {

	Vote.aggregate(

		[{ "$group": {
			"_id": "$name",
			"total_vote": { "$sum": 1 }
		}}],

		function(err, results) {
			if (err) throw err;

			socket.emit('vote', results);
		}
		);

});

// Start
server.listen(3000);
console.log('Open http://localhost:3000');

Pada baris ke 5-21 kita menambahkan event handler socket io, ketika ada client terhubung jalankan kode pengambilan data total voting kemudian emit/broadcast datanya ke semua client. Data tersebut akan digunakan untuk mengisi chart ketika pertama kali load.

Selanjutnya pada baris 24-25 kita jalankan server pada port 3000 dan tampilkan log nya.

Membuat Halaman Statis berisi Chart dengan Integrasi Socket IO

Mari kita lanjutkan pada program sisi client. Buatlah file index.html, kemudian simpan file tersebut di dalam folder "public". Isi dengan kode chart sederhana berikut ini :

<html>
<head>
   <style>
   .vote-result-wrapper{
      width: 700px;
      height: 700px;
   }
   </style>
</head>

<body>
   <h1>Quick Vote</h1>
   <div class="vote-result-wrapper">
      <canvas id="vote-result"></canvas>
   </div>
</body>

<script src="https://code.jquery.com/jquery-3.1.0.min.js" integrity="sha256-cCueBR6CsyA4/9szpPfrX3s49M9vUU5BgtiJj06wt/s=" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/1.4.8/socket.io.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.2.2/Chart.min.js"></script>

<script>
// Setting socket
var socket = io();
var data;
var myChart;
var context;

socket.on('vote', function (response) {

   var i = 0;

  	for (var key in response) {
      data.labels[i] = response[i]._id;
      data.datasets[0].data[i] = response[i].total_vote;
      i++;
   }
   
  	// Update chart
   myChart.update(); 
});

// Chart Data
data = {
   labels: [],
   datasets: [
        {
            label: [],
            data: [],
        }
    ]
};

context = document.getElementById('vote-result').getContext('2d');
myChart = new Chart(context,{
   type: 'bar',
   data: data,
   animation:{ 
      animateScale:true
   }
});

</script>
</html>

Dalam membuat chart di ChartJs kita membutuhkan elemen canvas. Elemen canvas tersebut juga membutuhkan atribut id untuk kemudian di define dalam library ChartJs. Dalam proyek ini kita sudah membuatnya pada baris ke 14.

Nah dalam sisi client kita juga butuh beberapa library seperti Jquery, SocketIO dan ChartJs. Bisa dilihat pada baris 18-20. Kita ambil library tersebut langsung dari CDN.

Pada baris 44-61 kita inisialisasi sebuah chart dengan membuat object baru dari library ChartJs. Variabel pada baris 44 adalah sebuah variabel config berisi Javascript object untuk menentukan presentasi data yang ditampilkan pada chart. Terlihat data tersebut kosong. Data kosong tersebut baru akan kita isi dalam event handler socket io on "vote" pada baris 33.

Coba lihat baris 40, itu adalah kode untuk mengupdate data chart. Kode ini akan dieksekusi jika ada data baru. Data baru akan diterima client pada saat server emit/broadcast. Lihatlah baris 29, ini adalah kode event handler dari socket io yang berfungsi mendengarkan perubahan yang terjadi.

Menjalankan Program

Semua folder dan file sudah dibuat, mari kita jalankan program ini, buka root dari proyek pada console teman-teman. Kemudian ketik perintah node app.js. Jika berhasil akan terlihat seperti ini :

Image

Kemudian buka url localhost dengan port 3000 dan lakukan pengisian data. Silahkan lakukan post ke url "http://localhost:3000/vote". Detailnya dapat dilihat pada gif berikut ini :

Image

Bisa kita lihat, ketika data kita update maka chart langsung berubah.

Demikian tutorial chart real-time sederhana ini. Teman-teman dapat mengunduh source code lengkap pada tautan berikut https://github.com/gemblue/RealtimeChart

Sekalian ada sedikit info, bagi teman-teman yang bisa NodeJs ada tantangan menarik berhadiah. Tantangan ini juga merupakan program reqruitment dari Pixel House Studio. Kunjungi artikelnya pada tautan berikut https://www.codepolitan.com/problemset-nodejs-dasar

Semoga bermanfaat!