In this tutorial, we'll guide you through building a real-time data dashboard using SuperViz, a powerful SDK for real-time communication and data synchronization. Real-time dashboards are an essential tool for monitoring and analyzing data as it happens, allowing users to make quick, informed decisions based on the latest information.

We'll use stock market data from Yahoo Finance as our example data source. However, the principles and techniques you'll learn can be applied to any data source you have access to on the server. Whether it's financial data, weather updates, or user activity, this tutorial will equip you with the skills to create a dynamic dashboard that updates in real-time.

By the end of this tutorial, you'll have a fully functioning real-time dashboard that can be customized and extended to meet your specific needs. Let's get started!

Prerequisite

To follow this tutorial, you will need a SuperViz account and a developer token. If you already have an account and a developer token, you can move on to the next step.

Create an account

To create an account, go to https://dashboard.superviz.com/register and create an account using either Google or an email/password. It's important to note that when using an email/password, you will receive a confirmation link that you'll need to click to verify your account.

Retrieving a Developer Token

To use the SDK, you’ll need to provide a developer token, as this token is essential for associating SDK requests with your account. You can retrieve both development and production SuperViz tokens from the dashboard..
Copy and save the developer token, as you will need it in the next steps of this tutorial.

Step 1: Setting Up the Server with Express.js

The server is responsible for fetching stock data from Yahoo Finance and broadcasting updates to the frontend using SuperViz.

1. Create a New Project and Install Dependencies

First, set up a new Node.js project and install the necessary packages for the server.

1
mkdir realtime-dashboard-server
2
cd realtime-dashboard-server
3
npm init -y
4
npm install express body-parser dotenv cors yahoo-finance2 node-fetch

Explanation:

  • express: A web application framework for setting up the server.
  • body-parser: Middleware to parse incoming JSON request bodies.
  • dotenv: Loads environment variables from a .env file.
  • cors: Middleware to enable Cross-Origin Resource Sharing.
  • yahoo-finance2: Library for fetching stock data from Yahoo Finance.
  • node-fetch: Fetch API for making HTTP requests.

2. Set Up the Express Server

Create a file named server.js and configure the server.

1
// server.js
2
import express from "express";
3
import bodyParser from "body-parser";
4
import cors from "cors";
5
import dotenv from "dotenv";
6
import yahooFinance from "yahoo-finance2";
7
8
dotenv.config();
9
10
const app = express();
11
app.use(bodyParser.json());
12
app.use(cors());
13
14
const subscriptions = [];

Explanation:

  • Express App: Create an Express application to handle requests.
  • Middlewares: Use bodyParser for JSON parsing and cors for handling cross-origin requests.
  • Subscriptions: An array to store active stock subscriptions, identified by room IDs.

3. Subscribe to Stock Updates

Define an endpoint to subscribe to stock updates.

1
app.get('/subscribe', async (req, res) => {
2
console.log(req.query)
3
4
if(!req.query.symbol) {
5
return res.status(400).json({ error: 'Missing symbol' })
6
}
7
8
try {
9
const response = await yahooFinance.quote(req.query.symbol)
10
11
if(!response) {
12
return res.status(404).json({ error: 'Symbol not found' })
13
}
14
15
subscriptions.push({
16
roomId: req.query.roomId,
17
symbol: req.query.symbol
18
})
19
20
return res.json({
21
symbol: response.symbol,
22
name: response.shortName,
23
price: response.regularMarketPrice,
24
change: response.regularMarketChange,
25
changePercent: response.regularMarketChangePercent,
26
high: response.regularMarketDayHigh,
27
low: response.regularMarketDayLow,
28
updatedAt: new Date().toISOString(),
29
})
30
} catch (error) {
31
return res.status(500).json({ error: error.toString() })
32
}
33
})

Explanation:

  • Subscribe Endpoint: A /subscribe endpoint allows clients to subscribe to stock updates by providing a symbol and roomId.
  • Yahoo Finance API: The yahooFinance.quote method fetches the stock data using the provided symbol.
  • Response Data: If the stock is found, it adds a subscription and returns detailed stock data.

4. Unsubscribe from Stock Updates

Provide a way to unsubscribe from stock updates.

1
app.get('/unsubscribe', (req, res) => {
2
if(!req.query.roomId) {
3
return res.status(400).json({ error: 'Missing roomId' })
4
}
5
6
if(!req.query.symbol) {
7
return res.status(400).json({ error: 'Missing symbol' })
8
}
9
10
const index = subscriptions.findIndex(subscription => subscription.roomId === req.query.roomId && subscription.symbol === req.query.symbol)
11
if(index === -1) {
12
return res.status(404).json({ error: 'Subscription not found' })
13
}
14
15
subscriptions.splice(index, 1)
16
return res.json({ message: 'Subscription removed' })
17
})

Explanation:

  • Unsubscribe Endpoint: A /unsubscribe endpoint allows clients to remove subscriptions by roomId.
  • Find Subscription: It locates the subscription using the roomId and removes it if found.

5. Send Stock Updates to SuperViz

Use a timed interval to fetch stock updates and broadcast them using the SuperViz API.

1
const interval = setInterval(async () => {
2
for(const subscription of subscriptions) {
3
try {
4
const stock = await yahooFinance.quote(subscription.symbol)
5
6
if(!stock) return
7
8
const data = {
9
symbol: stock.symbol,
10
name: stock.shortName,
11
price: stock.regularMarketPrice,
12
change: stock.regularMarketChange,
13
changePercent: stock.regularMarketChangePercent,
14
high: stock.regularMarketDayHigh,
15
low: stock.regularMarketDayLow,
16
updatedAt: new Date().toISOString(),
17
}
18
const channelId = 'stock-price'
19
const roomId = subscription.roomId
20
21
const response = await fetch(`https://nodeapi.superviz.com/realtime/${roomId}/${channelId}/publish`, {
22
method: 'POST',
23
headers: {
24
'Content-Type': 'application/json',
25
'apiKey': process.env.VITE_SUPERVIZ_API_KEY,
26
},
27
body: JSON.stringify({
28
name: 'stock-update',
29
data,
30
})
31
})
32
33
console.log(`Sending data to ${channelId}, stock: ${stock.symbol}`, response.status)
34
} catch (error) {
35
console.error(error)
36
}
37
}
38
}, 2000)

Explanation:

  • Interval: Every 2 seconds, fetch stock updates for all subscriptions.
  • SuperViz API: Send stock data to SuperViz's real-time API, using the roomId and channelId to target specific clients.

6. Start the Server

Launch the server to listen for requests.

1
app.listen(3000, () => {
2
console.log("Server is running on <http://localhost:3000>");
3
});

Explanation:

  • Server Listening: The server listens on port 3000 and logs a confirmation message when it's running.

Step 2: Setting Up the Frontend with React

The frontend uses React to display real-time stock updates by connecting to the SuperViz Real-Time Data Engine.

1. Create a New React Project

Initialize a new React application using Create React App with TypeScript.

1
npm create vite@latest
2
cd realtime-dashboard-frontend

2. Install SuperViz SDK

Add SuperViz and other necessary packages.

1
npm install @superviz/sdk luxon uuid

Explanation:

  • @superviz/sdk: SDK for real-time collaboration features.
  • luxon: DateTime library for formatting dates.
  • uuid: Library for generating unique identifiers.

3. Configure tailwind

In this tutorial, we'll use the Tailwind css framework. First, install the tailwind package.

1
npm install -D tailwindcss postcss autoprefixer
2
npx tailwindcss init -p

We then need to configure the template path. Open tailwind.config.js in the root of the project and insert the following code.

1
/** @type {import('tailwindcss').Config} */
2
export default {
3
content: [
4
"./index.html",
5
"./src/**/*.{js,ts,jsx,tsx}",
6
],
7
theme: {
8
extend: {},
9
},
10
plugins: [],
11
}

Then we need to add the tailwind directives to the global CSS file. (src/index.css)

1
@tailwind base;
2
@tailwind components;
3
@tailwind utilities;

4. Set Up Environment Variables

Create a .env file in the frontend directory and add the SuperViz API key.

1
VITE_SUPERVIZ_API_KEY=YOUR_SUPERVIZ_API_KEY

Explanation:

  • Environment Variables: Store the API key securely using .env and access it through import.meta.env.

5. Define Common Types

Create a directory src/common and add a types.ts file to define types used across the application.

1
typescriptCopy code
2
// src/common/types.ts
3
export type Stock = {
4
symbol: string;
5
name: string;
6
price: number;
7
change: number;
8
changePercent: number;
9
high: number;
10
low: number;
11
updatedAt: string;
12
};

Explanation:

  • Stock Type: Defines the structure for stock data, ensuring consistent use throughout the application.

6. Implement the Main App Component

Open src/App.tsx and set up the main component to handle user interactions and display stock data.

1
import { v4 as generateId } from "uuid";
2
import { useCallback, useEffect, useState } from "react";
3
import SuperVizRoom, {
4
Realtime,
5
RealtimeComponentEvent,
6
RealtimeMessage,
7
} from "@superviz/sdk";
8
import { Stock } from "./common/types";
9
import { StockPrice } from "./components/stock-price";
10
11
const apiKey = import.meta.env.VITE_SUPERVIZ_API_KEY as string;
12
const ROOM_ID = generateId();
13
14
export default function App() {
15
const [stock, setStock] = useState('AAPL')
16
const [stockList, setStockList] = useState<Stock[]>([])
17
18
const subscribeToStock = useCallback(async () => {
19
const params = {
20
roomId: ROOM_ID,
21
symbol: stock,
22
}
23
24
const url = new URL('http://localhost:3000/subscribe')
25
url.search = new URLSearchParams(params).toString()
26
27
const response = await fetch(url, {
28
method: 'GET',
29
headers: {
30
'Content-Type': 'application/json',
31
}
32
})
33
34
const data = await response.json()
35
36
if(!data || stockList.includes(data.symbol)) return
37
38
setStockList((prev) => [...prev, data])
39
}, [stock, stockList])
40
}

Explanation:

  • State Management: stock holds the input symbol, while stockList maintains the list of subscribed stocks.
  • Subscribe Function: Constructs a URL with query parameters and fetches stock data from the server. Updates the state if the stock isn't already subscribed.

7. Initialize SuperViz for Real-Time data synchronization

Add the initialization logic to connect to SuperViz.

1
const initialize = useCallback(async () => {
2
const superviz = await SuperVizRoom(apiKey, {
3
roomId: ROOM_ID,
4
participant: {
5
id: generateId(),
6
name: "participant-name",
7
},
8
group: {
9
id: "realtime-data-dashboard",
10
name: "realtime-data-dashboard",
11
},
12
});
13
14
const realtime = new Realtime();
15
superviz.addComponent(realtime);
16
17
realtime.subscribe(RealtimeComponentEvent.REALTIME_STATE_CHANGED, () => {
18
const channel = realtime.connect("stock-price");
19
channel.subscribe("stock-update", (message: RealtimeMessage) => {
20
console.log("New channel event", message);
21
22
if (typeof message.data !== "object") return;
23
24
setStockList((prev) => {
25
return prev.map((stock) => {
26
const newStock = message.data as Stock;
27
28
if (stock.symbol === newStock?.symbol) {
29
return newStock;
30
}
31
32
return stock;
33
});
34
});
35
});
36
});
37
}, []);

Explanation:

  • SuperViz Initialization: Connects to the SuperViz room using an API key and room details. Adds the Realtime component to handle real-time events.
  • Realtime Subscription: Subscribes to stock-update events, updating the stock list with new data.

8. Render the UI Components

Finish the App component by rendering the user interface.

1
return (
2
<div className="w-full h-full bg-gray-200 flex items-center justify-center flex-col">
3
<header className="w-full p-5 bg-purple-400 flex items-center justify-between">
4
<div>
5
<h1 className="text-white text-2xl font-bold">
6
Realtime Data Dashboard
7
</h1>
8
</div>
9
<div>
10
<input
11
type="text"
12
value={stock}
13
onChange={(e) => setStock(e.target.value)}
14
className="p-2 border border-gray-400 rounded-md focus:outline-none focus:border-purple-400"
15
/>
16
<button
17
onClick={subscribeToStock}
18
className="p-2 bg-purple-500 text-white rounded-md ml-2 focus:outline-none"
19
>
20
Subscribe
21
</button>
22
</div>
23
</header>
24
<main className="flex-1 p-20 flex w-full gap-2">
25
{stockList.map((stock) => (
26
<StockPrice key={stock.symbol} stock={stock} />
27
))}
28
</main>
29
</div>
30
);

Explanation:

  • UI Structure: The UI contains an input for stock symbols and a subscribe button. The StockPrice component is used to display each stock's details.
  • State Updates: The onChange event updates the stock input, while clicking the button triggers the subscription logic.

9. Implement the StockPrice Component

Create a src/components directory and add a stock-price.tsx file to define how each stock is displayed.

1
// src/components/stock-price.tsx
2
import { Stock } from "../common/types";
3
import { DateTime } from "luxon";
4
5
type Props = {
6
stock: Stock;
7
};
8
9
export function StockPrice({ stock }: Props) {
10
const formatPrice = (price: number) => {
11
return new Intl.NumberFormat("en-US", {
12
style: "currency",
13
currency: "USD",
14
}).format(price);
15
};
16
17
const formatDateTime = (date: string) => {
18
return DateTime.fromISO(date).toFormat("DD HH:mm:ss");
19
};
20
21
return (
22
<div className="p-6 border border-solid border-gray-300 bg-white min-w-40 rounded-xl h-fit shadow-md">
23
<h1>{stock.name}</h1>
24
<p>{stock.symbol}</p>
25
<p>Current Price: {formatPrice(stock.price)}</p>
26
<p>Lower Price: {formatPrice(stock.low)}</p>
27
<p>Higher Price: {formatPrice(stock.high)}</p>
28
<p>Updated At: {formatDateTime(stock.updatedAt)}</p>
29
</div>
30
);
31
}

Explanation:

  • StockPrice Component: Displays detailed stock information, including the name, symbol, current price, low/high prices, and updated timestamp.
  • Formatting Functions: formatPrice and formatDateTime ensure the prices and dates are displayed in a user-friendly format.

Step 3: Running the Application

To start the application, run this command in the terminal:

1
npm run dev

This command will start both the server and the frontend application.

You can access the frontend application at http://localhost:5173 and the server at http://localhost:3000.

Summary

In this tutorial, we've built a real-time stock dashboard using SuperViz, Express.js, and React. The server fetches stock data from Yahoo Finance and sends updates to subscribed clients using SuperViz's real-time API. The frontend subscribes to stock updates, displaying them in a responsive UI. By following these steps, you can customize the dashboard to track different stocks, add more features, and deploy it to a production environment.

Feel free to refer to the full code in the GitHub repository for more details.