Membangun Grafik Real-Time Dengan GraphQL Dan Postgres
Diterbitkan: 2022-03-10Bagan merupakan bagian integral dari industri apa pun yang berhubungan dengan data. Bagan berguna dalam industri pemungutan suara dan pemungutan suara, dan juga sangat bagus dalam membantu kami lebih memahami berbagai perilaku dan karakteristik pengguna dan klien yang bekerja sama dengan kami.
Mengapa grafik real-time begitu penting? Yah, mereka berguna dalam kasus-kasus ketika data baru diproduksi terus menerus; misalnya, saat menggunakan rangkaian waktu langsung untuk memvisualisasikan harga saham adalah penggunaan yang bagus untuk grafik waktu nyata. Dalam tutorial ini, saya akan menjelaskan bagaimana membangun grafik real-time dengan teknologi open-source yang tepat untuk tugas khusus ini.
Catatan : Tutorial ini membutuhkan pengetahuan dasar tentang React dan GraphQL.
Tumpukan
- PostgreSQL
Inti dari penggunaan Bagan adalah untuk memvisualisasikan data volume "besar". Oleh karena itu, kami membutuhkan database yang secara efisien menangani data besar dan menyediakan API intuitif untuk merestrukturisasinya. Database SQL memungkinkan kita untuk membuat tampilan yang abstrak dan data agregat untuk kita. Kami akan menggunakan Postgres yang merupakan database yang telah teruji waktu dan sangat efisien. Ini juga memiliki ekstensi sumber terbuka yang mewah seperti Timescale dan PostGIS yang memungkinkan kita membangun grafik berbasis geolokasi dan deret waktu masing-masing. Kami akan menggunakan Skala Waktu untuk membangun bagan deret waktu kami. - Mesin GraphQL
Posting ini adalah tentang membangun grafik waktu nyata, dan GraphQL hadir dengan spesifikasi yang terdefinisi dengan baik untuk langganan waktu nyata. Hasura GraphQL Engine adalah server GraphQL open-source yang mengambil koneksi Postgres dan memungkinkan Anda untuk menanyakan data Postgres melalui GraphQL waktu nyata. Itu juga dilengkapi dengan lapisan kontrol akses yang membantu Anda membatasi data Anda berdasarkan aturan kontrol akses khusus. - ChartJS
ChartJS adalah pustaka sumber terbuka yang populer dan terawat dengan baik untuk membuat bagan dengan JavaScript. Kami akan menggunakanchart.js
bersama dengan abstraksi ReactJS-nyareact-chartjs-2
. Tentang mengapa React, itu karena React memberdayakan pengembang dengan API berbasis peristiwa yang intuitif. Selain itu, aliran data searah React sangat ideal untuk membuat bagan yang digerakkan oleh data.
Persyaratan
Untuk tutorial ini, Anda memerlukan yang berikut ini di sistem Anda:
- Docker CE
Docker adalah perangkat lunak yang memungkinkan Anda menyimpan aplikasi Anda. Gambar buruh pelabuhan adalah paket independen yang berisi perangkat lunak beserta dependensinya dan sistem operasi minimalis. Gambar buruh pelabuhan seperti itu secara teknis dapat dijalankan di mesin apa pun yang telah memasang buruh pelabuhan. Anda akan membutuhkan buruh pelabuhan untuk tutorial ini.- Baca lebih lanjut tentang Docker
- Instal Docker
- npm: npm adalah paket yang dikelola untuk JavaScript.
Demo
Kami akan membuat bagan deret waktu langsung berikut yang menunjukkan suhu maksimum suatu lokasi dalam interval 5 detik selama 20 menit terakhir dari saat ini.

Menyiapkan Backend
Menjalankan Layanan
Backend terdiri dari database Postgres, ekstensi skala waktunya, dan Hasura GraphQL Engine. Mari kita menjalankan database dan server GraphQL kita dengan menjalankan gambar buruh pelabuhan masing-masing. Buat file bernama docker-compose.yaml
dan tempel konten ini ke dalamnya.
Catatan : docker-compose
adalah utilitas untuk menjalankan beberapa gambar buruh pelabuhan secara deklaratif.
version: '2' services: timescale: image: timescale/timescaledb:latest-pg10 restart: always environment: POSTGRES_PASSWORD: postgrespassword volumes: - db_data:/var/lib/postgresql/data graphql-engine: image: hasura/graphql-engine:v1.0.0-alpha38 ports: - "8080:8080" depends_on: - "timescale" restart: always environment: HASURA_GRAPHQL_DATABASE_URL: postgres://postgres:postgrespassword@timescale:5432/postgres HASURA_GRAPHQL_ACCESS_KEY: mylongsecretkey command: - graphql-engine - serve - --enable-console volumes: db_data:
docker-compose.yaml
ini berisi spesifikasi untuk dua layanan:
-
timescale
Ini adalah database Postgres kami dengan ekstensi Timescale terpasang. Ini dikonfigurasi untuk berjalan di port 5432. -
graphql-engine
Ini adalah contoh Hasura GraphQL Engine kami, yaitu server GraphQL yang menunjuk ke database dan memberikan GraphQL API di atasnya. Itu dikonfigurasi untuk berjalan di port 8080, dan port 8080 dipetakan ke port 8080 dari mesin tempat kontainer buruh pelabuhan ini berjalan. Ini berarti Anda dapat mengakses server GraphQL ini melalui dilocalhost:8080
mesin.
Mari jalankan container docker ini dengan menjalankan perintah berikut di mana pun Anda meletakkan docker-compose.yaml
.
docker-compose up -d
Perintah ini menarik gambar buruh pelabuhan dari cloud dan menjalankannya dalam urutan yang diberikan. Mungkin perlu beberapa detik berdasarkan kecepatan internet Anda. Setelah selesai, Anda dapat mengakses konsol GraphQL Engine Anda di https://localhost:8080/console
.

Menyiapkan Basis Data
Selanjutnya, mari kita buat tabel yang disebut suhu yang menyimpan nilai suhu pada waktu yang berbeda. Buka tab Data di konsol dan buka bagian SQL
. Buat tabel temperature
kami dengan menjalankan blok SQL ini:
CREATE TABLE temperature ( temperature numeric not null, location text not null, recorded_at timestamptz not null default now() );
Ini membuat tabel Postgres sederhana dalam database. Tetapi kami ingin memanfaatkan partisi interval waktu dari ekstensi Timescale. Untuk melakukan ini, kita harus mengubah tabel ini menjadi hypertable skala waktu dengan menjalankan perintah SQL:
SELECT create_hypertable('temperature', 'recorded_at');
Perintah ini membuat hypertable yang dipartisi berdasarkan waktu di bidang recorded_at
.
Sekarang, sejak tabel ini dibuat, kita dapat langsung mulai membuat kueri GraphQL di atasnya. Anda dapat mencobanya dengan mengklik tab GraphiQL
di atas. Coba buat mutasi dulu:
mutation { insert_temperature ( objects: [{ temperature: 13.4 location: "London" }] ) { returning { recorded_at temperature } } }
Mutasi GraphQL di atas menyisipkan baris dalam tabel temperature
. Sekarang coba buat kueri GraphQL untuk memeriksa apakah data telah dimasukkan.
Kemudian coba buat kueri:
query { temperature { recorded_at temperature location } }
Semoga berhasil :)
Sekarang, tugas kami adalah membuat bagan deret waktu langsung yang menunjukkan suhu maksimum suatu lokasi dalam interval 5 detik selama 20 menit terakhir dari saat ini. Mari buat tampilan yang memberi kita data ini.
CREATE VIEW last_20_min_temp AS ( SELECT time_bucket('5 seconds', recorded_at) AS five_sec_interval, location, MAX(temperature) AS max_temp FROM temperature WHERE recorded_at > NOW() - interval '20 minutes' GROUP BY five_sec_interval, location ORDER BY five_sec_interval ASC );
Tampilan ini mengelompokkan data dari tabel temperature
dalam jendela 5 detik dengan suhu maksimumnya ( max_temp)
. Pengelompokan sekunder dilakukan dengan menggunakan field location
. Semua data ini hanya dari dua puluh menit terakhir dari saat ini.
Itu dia. Backend kami sudah diatur. Sekarang mari kita buat grafik real-time yang bagus.
Paling depan
Halo Langganan GraphQL
Langganan GraphQL pada dasarnya adalah kueri GraphQL "langsung". Mereka beroperasi melalui WebSockets dan memiliki struktur respons yang persis sama seperti kueri GraphQL. Kembali ke https://localhost:8080/console
dan coba berlangganan GraphQL ke tampilan yang kita buat.
subscription { last_20_min_temp( order_by: { five_sec_interval: asc } where: { location: { _eq: "London" } } ) { five_sec_interval location max_temp } }
Langganan ini berlangganan data dalam tampilan di mana lokasinya adalah London
dan diurutkan dalam urutan five_second_intervals
.
Secara alami, respons dari tampilan akan menjadi array kosong karena kami belum memasukkan apa pun ke dalam database dalam dua puluh menit terakhir. (Anda mungkin melihat entri yang kami masukkan beberapa waktu lalu jika Anda mencapai bagian ini dalam waktu dua puluh menit.)

{ "data": { "last_20_min_temp": [] } }
Dengan tetap berlangganan ini, buka tab lain dan coba masukkan nilai lain di tabel temperatures
menggunakan mutasi yang sama yang kami lakukan sebelumnya. Setelah memasukkan, jika Anda kembali ke tab tempat langganan aktif, Anda akan melihat respons telah diperbarui secara otomatis. Itulah keajaiban waktu nyata yang disediakan oleh GraphQL Engine. Mari gunakan langganan ini untuk memperkuat grafik real-time kita.
Memulai Dengan Create-React-App
Mari kita mulai dengan cepat memulai aplikasi React menggunakan create react app. Jalankan perintah:
npx create-react-app time-series-chart
Ini akan membuat proyek starter kosong. cd
ke dalamnya dan instal GraphQL dan pustaka bagan. Juga, instal momen untuk mengonversi cap waktu ke format yang dapat dibaca manusia.
cd time-series-chart npm install --save apollo-boost apollo-link-ws subscriptions-transport-ws graphql react-apollo chart.js react-chartjs-2 moment
Terakhir, jalankan aplikasi dengan npm start
dan aplikasi React dasar akan terbuka di https://localhost:3000
.

Menyiapkan Klien Apollo Untuk GraphQL Sisi Klien
Klien Apollo saat ini adalah klien GraphQL terbaik yang bekerja dengan server yang sesuai dengan GraphQL. Relay modern juga bagus tetapi server harus mendukung spesifikasi relai untuk memanfaatkan semua manfaat Relay modern. Kami akan menggunakan klien Apollo untuk GraphQL sisi klien untuk tutorial ini. Mari kita lakukan penyiapan untuk menyediakan klien Apollo ke aplikasi.
Saya tidak masuk ke seluk-beluk pengaturan ini karena cuplikan kode berikut diambil langsung dari dokumen. Buka src/index.js
di direktori aplikasi React dan buat instance klien Apollo dan tambahkan cuplikan kode ini di atas ReactDOM.render
.
import { WebSocketLink } from 'apollo-link-ws'; import { ApolloClient } from 'apollo-client'; import { ApolloProvider } from 'react-apollo'; import { InMemoryCache } from 'apollo-cache-inmemory'; // Create a WebSocket link: const link = new WebSocketLink({ uri: `ws://localhost:8080/v1alpha1/graphql`, options: { reconnect: true, connectionParams: { headers: { "x-hasura-admin-secret: "mylongsecretkey" } } } }) const cache = new InMemoryCache(); const client = new ApolloClient({ link, cache });
Terakhir, bungkus App
di dalam ApolloProvider
sehingga kita dapat menggunakan klien Apollo di komponen anak-anak. App.js
Anda akhirnya akan terlihat seperti:
import React from 'react'; import ReactDOM from 'react-dom'; import './index.css'; import App from './App'; import { WebSocketLink } from 'apollo-link-ws'; import { ApolloClient } from 'apollo-client'; import { ApolloProvider } from 'react-apollo'; import { InMemoryCache } from 'apollo-cache-inmemory'; // Create a WebSocket link: const link = new WebSocketLink({ uri: `ws://localhost:8080/v1alpha1/graphql`, options: { reconnect: true, connectionParams: { headers: { "x-hasura-admin-secret: "mylongsecretkey" } } } }) const cache = new InMemoryCache(); const client = new ApolloClient({ link, cache }); ReactDOM.render( ( <ApolloProvider client={client}> <App /> </ApolloProvider> ), document.getElementById('root') );
Klien Apollo telah disiapkan. Kami sekarang dapat dengan mudah menggunakan GraphQL real-time dari Aplikasi kami. src/App.js
.
Membangun Bagan
ChartJS menyediakan API yang cukup rapi untuk membuat bagan. Kami akan membangun grafik garis; jadi bagan garis mengharapkan data dari formulir:
{ "labels": ["label1", "label2", "label3", "label4"], "datasets": [{ "label": "Sample dataset", "data": [45, 23, 56, 55], "pointBackgroundColor": ["red", "brown", "green", "yellow"], "borderColor": "brown", "fill": false }], }
Jika dataset di atas digunakan untuk merender diagram garis, akan terlihat seperti ini:

Mari kita coba membuat bagan sampel ini terlebih dahulu. Impor Line
dari react-chartjs-2
dan buat itu melewati objek di atas sebagai penyangga data. Metode render akan terlihat seperti:
render() { const data = { "labels": ["label1", "label2", "label3", "label4"], "datasets": [{ "label": "Sample dataset", "data": [45, 23, 56, 55], "pointBackgroundColor": ["red", "brown", "green", "yellow"], "borderColor": "brown", "fill": false }], } return ( <div style={{display: 'flex', alignItems: 'center', justifyContent: 'center', margin: '20px'}} > <Line data={data} /> </div> ); }
Selanjutnya, kami akan berlangganan data dalam tampilan kami dan memasukkannya ke bagan Garis. Tapi bagaimana kita melakukan langganan pada klien?
Komponen <Subscription>
Apollo bekerja menggunakan pola render prop di mana anak-anak komponen dirender dengan konteks data langganan.
<Subscription subscription={gql`subscription { parent { child } }`} /> { ({data, error, loading}) => { if (error) return <Error error={error} />; if (loading) return <Loading />; return <RenderData data={data} />; } } </Subscription>
Mari kita menggunakan salah satu komponen Subscription
tersebut untuk berlangganan tampilan kita dan kemudian mengubah data langganan ke struktur yang diharapkan ChartJS. Logika transformasi terlihat seperti ini:
let chartJSData = { labels: [], datasets: [{ label: "Max temperature every five seconds", data: [], pointBackgroundColor: [], borderColor: 'brown', fill: false }] }; data.last_20_min_temp.forEach((item) => { const humanReadableTime = moment(item.five_sec_interval).format('LTS'); chartJSData.labels.push(humanReadableTime); chartJSData.datasets[0].data.push(item.max_temp); chartJSData.datasets[0].pointBackgroundColor.push('brown'); })
Catatan : Anda juga dapat menggunakan pustaka sumber terbuka graphq2chartjs untuk mengubah data dari respons GraphQL ke bentuk yang diharapkan ChartJS.
Setelah menggunakan ini di dalam komponen Langganan, App.js
kami terlihat seperti:
import React, { Component } from 'react'; import { Line } from 'react-chartjs-2'; import { Subscription } from 'react-apollo'; import gql from 'graphql-tag'; import moment from 'moment'; const TWENTY_MIN_TEMP_SUBSCRIPTION= gql' subscription { last_20_min_temp( order_by: { five_sec_interval: asc } where: { location: { _eq: "London" } } ) { five_sec_interval location max_temp } } ' class App extends Component { render() { return ( <div style={{display: 'flex', alignItems: 'center', justifyContent: 'center', margin: '20px'}} > <Subscription subscription={TWENTY_MIN_TEMP_SUBSCRIPTION}> { ({data, error, loading}) => { if (error) { console.error(error); return "Error"; } if (loading) { return "Loading"; } let chartJSData = { labels: [], datasets: [{ label: "Max temperature every five seconds", data: [], pointBackgroundColor: [], borderColor: 'brown', fill: false }] }; data.last_20_min_temp.forEach((item) => { const humanReadableTime = moment(item.five_sec_interval).format('LTS'); chartJSData.labels.push(humanReadableTime); chartJSData.datasets[0].data.push(item.max_temp); chartJSData.datasets[0].pointBackgroundColor.push('brown'); }) return ( <Line data={chartJSData} options={{ animation: {duration: 0}, scales: { yAxes: [{ticks: { min: 5, max: 20 }}]} }} /> ); } } </Subscription> </div> ); } } export default App;
Anda akan memiliki bagan waktu nyata yang berfungsi penuh di https://localhost:3000
. Namun, itu akan kosong, jadi mari kita isi beberapa data sampel sehingga kita benar-benar dapat melihat keajaiban terjadi.
Catatan : Saya telah menambahkan beberapa opsi lagi ke bagan Garis karena saya tidak suka animasi mewah di ChartJS. Deret waktu terlihat manis ketika sederhana, namun, Anda dapat menghapus opsi prop jika Anda mau.
Memasukkan Data Sampel
Mari kita menulis skrip yang mengisi database kita dengan data dummy. Buat direktori terpisah (di luar aplikasi ini) dan buat file bernama script.js
dengan konten berikut,
const fetch = require('node-fetch'); setInterval( () => { const randomTemp = (Math.random() * 5) + 10; fetch( `https://localhost:8080/v1alpha1/graphql`, { method: 'POST', body: JSON.stringify({ query: ` mutation ($temp: numeric) { insert_temperature ( objects: [{ temperature: $temp location: "London" }] ) { returning { recorded_at temperature } } } `, variables: { temp: randomTemp } }) } ).then((resp) => resp.json().then((respObj) => console.log(JSON.stringify(respObj, null, 2)))); }, 2000 );
Sekarang jalankan dua perintah ini:
npm install --save node-fetch node script.js
Anda dapat kembali ke https://localhost:3000
dan melihat pembaruan grafik.
Menyelesaikan
Anda dapat membuat sebagian besar grafik waktu nyata menggunakan ide-ide yang telah kita diskusikan di atas. Algoritmanya adalah:
- Terapkan Mesin GraphQL dengan Postgres;
- Buat tabel tempat Anda ingin menyimpan data;
- Berlangganan tabel tersebut dari aplikasi React Anda;
- Render grafik.
Anda dapat menemukan kode sumber di sini.