Membangun Grafik Real-Time Dengan GraphQL Dan Postgres

Diterbitkan: 2022-03-10
Ringkasan cepat Tidak ada cara yang lebih baik untuk memahami data selain dengan memvisualisasikannya dengan bagan dan diagram. Komunitas JS memiliki beberapa proyek sumber terbuka yang hebat yang membuat visualisasi data lebih mudah, namun, belum ada solusi masuk untuk membangun backend waktu nyata yang dapat mendukung bagan ini dan menjadikannya waktu nyata. Dengan GraphQL (yang memiliki spesifikasi yang terdefinisi dengan baik untuk langganan real-time), kita bisa menjalankan backend real-time dalam hitungan detik dan menggunakannya untuk mendukung grafik real-time.

Bagan 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

  1. 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.
  2. 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.
  3. ChartJS
    ChartJS adalah pustaka sumber terbuka yang populer dan terawat dengan baik untuk membuat bagan dengan JavaScript. Kami akan menggunakan chart.js bersama dengan abstraksi ReactJS-nya react-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.
Lebih banyak setelah melompat! Lanjutkan membaca di bawah ini

Persyaratan

Untuk tutorial ini, Anda memerlukan yang berikut ini di sistem Anda:

  1. 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
  2. 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.

Demo GIF dari grafik waktu nyata
Demo GIF dari grafik waktu nyata

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:

  1. timescale
    Ini adalah database Postgres kami dengan ekstensi Timescale terpasang. Ini dikonfigurasi untuk berjalan di port 5432.
  2. 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 di localhost: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 .

Konsol Mesin Hasura GraphQL
Konsol Hasura GraphQL Engine (Pratinjau besar)

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 .

Buat-reaksi-aplikasi mentah
Aplikasi kreasi-reaksi mentah (Pratinjau besar)

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:

Contoh diagram garis
Contoh diagram garis (Pratinjau besar)

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:

  1. Terapkan Mesin GraphQL dengan Postgres;
  2. Buat tabel tempat Anda ingin menyimpan data;
  3. Berlangganan tabel tersebut dari aplikasi React Anda;
  4. Render grafik.

Anda dapat menemukan kode sumber di sini.