Real‑Time Stock Tracker Clone

A minimal TypeScript/Node.js app that streams live prices using a free stock API

Welcome to another official UVF IT guide! In this tutorial we’ll build a tiny real‑time stock tracker inspired by Bolt Forge’s live data agent, using TypeScript, Node.js, and a free public stock price endpoint.

You’ll have a working web page that shows the latest price for a ticker and updates every few seconds – all in under an hour and without any paid services.

Before you start

Architecture Overview
LOCAL DEVELOPMENT fetch GET price Node.js + Express Server HTML/JS Front‑end TwelveData Free API cloud
1

Phase 1: Scaffold the project

Create a fresh npm workspace with TypeScript and Express. This gives us a clean folder, type safety, and a lightweight web server to expose our price endpoint.

We’ll also add nodemon for automatic restarts during development, so you can see changes instantly.

mkdir stock-tracker && cd stock-tracker
npm init -y
npm install express@4.18.2
npm install -D typescript@5.4.5 ts-node@10.9.1 @types/express@4.17.21 nodemon@3.0.3
npx tsc --init --rootDir src --outDir dist --esModuleInterop true --module commonjs --target es2022
mkdir src
cat > src/.gitkeep <<'EOF'
EOF
2

Phase 2: Define the price‑fetch route (model)

Add a single endpoint that calls the free TwelveData API and returns JSON. The route reads a ticker from the query string, contacts the external service, and streams the latest price back to the client.

Keeping the logic in one file makes the clone easy to understand, and TypeScript will catch any mismatched types before you run.

src/server.ts

import express, { Request, Response } from 'express';
import fetch from 'node-fetch';

const app = express();
const PORT = process.env.PORT || 3000;
const API_KEY = 'demo'; // TwelveData free demo key

app.get('/price', async (req: Request, res: Response) => {
  const symbol = (req.query.ticker as string) || 'AAPL';
  const url = `https://api.twelvedata.com/price?symbol=${encodeURIComponent(symbol)}&apikey=${API_KEY}`;
  try {
    const response = await fetch(url);
    const data = await response.json();
    res.json({ symbol, price: data.price ?? null });
  } catch (err) {
    res.status(500).json({ error: 'Failed to fetch price' });
  }
});

app.listen(PORT, () => console.log(`Server running on http://localhost:${PORT}`));
3

Phase 3: Build a tiny UI (API/UI)

We’ll create a minimal front‑end that shows the latest price for a hard‑coded ticker. The page will request our /price endpoint every five seconds, parse the JSON, and update the DOM. Keeping the UI tiny lets you focus on the data flow without pulling in heavy frameworks. This also demonstrates how the back‑end can be consumed by any client.

public/index.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <title>Real‑Time Stock Tracker</title>
  <style>
    body {font-family:Arial,sans-serif; margin:2rem;}
    #price {font-size:2rem; color:#2c3e50;}
  </style>
</head>
<body>
  <h1>MSFT Price</h1>
  <div id="price">Loading...</div>
  <script>
    const fetchPrice = async () => {
      try {
        const res = await fetch('/price');
        const data = await res.json();
        document.getElementById('price').textContent = `$${data.price.toFixed(2)}`;
      } catch (e) { console.error(e); }
    };
    fetchPrice();
    setInterval(fetchPrice, 5000);
  </script>
</body>
</html>
4

Phase 4: Serve static files & run locally

Now we tell Express to serve the public folder so the browser can load our HTML page. We also keep the existing /price route that pulls data from the free stock API. Finally, we add a dev script that runs the server with ts-node-dev for hot‑reloading during development.

src/server.ts

import express from 'express';
import fetch from 'node-fetch';

const app = express();
const PORT = 3000;
// Hard‑coded ticker symbol – you can change this later
const SYMBOL = 'MSFT';

// Serve static UI
app.use(express.static('public'));

// API endpoint returning the latest price
app.get('/price', async (_req, res) => {
  try {
    const apiKey = process.env.FINNHUB_API_KEY || 'demo';
    const response = await fetch(`https://finnhub.io/api/v1/quote?symbol=${SYMBOL}&token=${apiKey}`);
    const json = await response.json();
    res.json({ price: json.c }); // `c' is current price
  } catch (err) {
    console.error(err);
    res.status(500).json({ error: 'Failed to fetch price' });
  }
});

app.listen(PORT, () => console.log(`Server running at http://localhost:${PORT}`));
5

Phase 5: Verify and extend

Start the dev server and open http://localhost:3000 – you should see the MSFT price updating every five seconds. To experiment, edit the SYMBOL constant in src/server.ts to another ticker (e.g., AAPL) and restart; the UI will now stream that symbol. From here you can add an input box to let users pick symbols or cache responses to reduce API calls.

npm install express node-fetch @types/express @types/node-fetch ts-node-dev typescript
npx ts-node-dev src/server.ts

Verifying the Stack

CheckCommand / Why it matters
npm install completed without errorsnpm list | grep express
Server starts and logs http://localhost:3000npx ts-node-dev src/server.ts
Browser shows a number for MSFT price and updates every 5 sOpen http://localhost:3000 in Chrome

You’re live!

Your minimal real‑time stock tracker is now running locally. Experiment by adding a ticker input box or caching responses – the foundation is yours to expand.