Published on

Building a Real-Time Stock Listings App with Server-Sent Events and TypeScript

Authors

Introduction

In the world of web development, real-time updates are increasingly becoming a standard requirement for modern web applications. Whether it's updating live scores, notifications, or stock prices, users expect to see changes reflected in real-time without needing to refresh the page. In this tutorial, we'll explore how to build a real-time stock listings app using Server-Sent Events (SSE) and TypeScript, two powerful technologies that enable seamless real-time communication between the server and the client.

What are Server-Sent Events (SSE)?

Server-Sent Events (SSE) is a web technology that allows servers to push real-time updates to clients over HTTP. Unlike other real-time communication protocols like WebSocket, SSE is based on a unidirectional flow of data from the server to the client. SSE is easy to implement and is supported by most modern web browsers, making it a great choice for scenarios where real-time updates are required without the need for bidirectional communication.

Setting Up the Project:

To get started, let's set up a new Node.js project and install the necessary dependencies:

mkdir stock-listing-app-using-sse
cd stock-listing-app-using-sse
npm init -y
npm install express express-handlebars faker tailwindcss @types/node @types/express

Project Structure

stock-listing-app-using-sse/
├── src/
│   ├── server.ts
│   ├── views/
│   │   └── index.hbs
│   └── public/
│       └── script.js
├── package.json
└── tsconfig.json

Building the Server:

We'll start by building the server-side code using TypeScript and Express.js. Create a file named server.ts inside the src directory with the following content:

import express, { Request, Response } from 'express'
import { faker } from '@faker-js/faker'
import http from 'http'
import path from 'path'
import { engine } from 'express-handlebars'

const app = express()
const server = http.createServer(app)

app.engine('hbs', engine({ extname: 'hbs' }))
app.set('view engine', 'hbs')
app.set('views', path.join(__dirname, 'views'))

app.use(express.static(path.join(__dirname, 'public')))

app.get('/events', (req: Request, res: Response) => {
  console.log('Client Connected!')
  res.writeHead(200, {
    'Content-Type': 'text/event-stream',
    'Cache-Control': 'no-cache',
    Connection: 'keep-alive',
  })

  const intervalId = setInterval(() => {
    const stocks = updateStockPrices()
    res.write(`data: ${JSON.stringify(stocks)}\n\n`)
  }, 750)

  req.on('close', () => {
    clearInterval(intervalId)
    console.log('Client disconnected')
  })
})

app.get('/', (req: Request, res: Response) => {
  res.render('index')
})

let stocks = generateStocks()

function updateStockPrices() {
  stocks.forEach((stock) => {
    stock.price = Math.floor(Math.random() * 480)
  })
  return stocks
}

function generateStocks() {
  const stocks = []
  for (let i = 0; i < 5000; i++) {
    const stock = {
      name: faker.company.name(),
      symbol: faker.finance.currencyCode(),
      price: Math.floor(Math.random() * 485),
    }
    stocks.push(stock)
  }
  return stocks
}

const PORT = process.env.PORT || 1337
server.listen(PORT, () => {
  console.log(`Server running on port ${PORT}`)
})

In this code:

  • We import the necessary modules such as Express, Handlebars, Faker, etc.
  • We set up Express.js with Handlebars as the view engine.
  • We define a route for the SSE endpoint /events that sends real-time updates of stock prices to clients.
  • We define a route for rendering the index page, which will contain the stock listings table.
  • We generate dummy stock listings using the Faker library and update their prices every 5 seconds.

Creating the Client:

Next, let's create the client-side code to display the stock listings table. Create a file named index.hbs inside the src/views directory with the following content:

<html>
  <head>
    <title>Stock Listings</title>
    <link
      href='https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css'
      rel='stylesheet'
    />
  </head>
  <body class='bg-gray-100 font-sans leading-normal tracking-normal'>

    <div class='container mx-auto px-4'>

      <h1 class='text-3xl py-4'>Stock Listings</h1>

      <div class='overflow-x-auto bg-white rounded-lg shadow overflow-y-auto relative'>
        <table
          class='border-collapse table-auto w-full whitespace-no-wrap bg-white table-striped relative'
        >
          <thead>
            <tr class='text-left'>
              <th class='py-2 px-3 bg-gray-200'>Name</th>
              <th class='py-2 px-3 bg-gray-200'>Symbol</th>
              <th class='py-2 px-3 bg-gray-200'>Price</th>
            </tr>
          </thead>
          <tbody id='stock-table' class='h-96 overflow-y-scroll overflow-hidden'>
          </tbody>
        </table>
      </div>

    </div>

    <script src='/script.js'></script>
  </body>
</html>

In this code:

  • We use Tailwind CSS to style the stock listings table.
  • We include a script tag to load the client-side JavaScript file script.js.

Now, let's create the client-side JavaScript file script.js inside the src/public directory with the following content:

const stockTableBody = document.querySelector('#stock-table')
const eventSource = new EventSource('/events')

eventSource.onmessage = function (event) {
  const stocks = JSON.parse(event.data)
  renderStocks(stocks)
}

eventSource.onerror = function (error) {
  console.error('SSE error:', error)
}

function renderStocks(stocks) {
  stockTableBody.innerHTML = ''

  stocks.forEach((stock) => {
    const row = document.createElement('tr')
    row.innerHTML = `
      <td class="border px-4 py-2">${stock.name}</td>
      <td class="border px-4 py-2">${stock.symbol}</td>
      <td class="border px-4 py-2">${stock.price}</td>
    `
    stockTableBody.appendChild(row)
  })
}

Pros and Cons

ProsCons
Real-time updates of stock pricesSSE has limited browser compatibility
Dynamic user interfaceSSE is unidirectional (server to client)
Efficient communication with serverSSE does not support binary data
Simple and straightforward to useSSE does not support custom headers
Customizable styling with Tailwind CSSSSE connections may be subject to timeouts
Scalable architectureSSE connections may be impacted by network fluctuations
Easily extensible and maintainableSSE connections can consume server resources if not managed properly

Conclusion

Server-Sent Events (SSE) offer a powerful solution for implementing real-time updates in web applications. With SSE, developers can establish a persistent connection between the server and the client, enabling the server to push data to the client in real-time without the need for continuous polling.

One of the key advantages of SSE is its simplicity and ease of use. Unlike other real-time communication protocols such as WebSockets, SSE requires minimal setup and is supported by most modern web browsers out of the box. This makes it an ideal choice for scenarios where real-time updates are required without the complexity of bidirectional communication.

SSE also offers several benefits over traditional polling techniques. By maintaining a persistent connection, SSE reduces the overhead associated with frequent HTTP requests, resulting in lower latency and improved efficiency. Additionally, SSE supports automatic reconnection, ensuring that the client remains connected to the server even in the event of network disruptions.

However, SSE does have some limitations to consider. For example, SSE is unidirectional, meaning that data can only be sent from the server to the client. This makes it less suitable for applications that require bidirectional communication, such as chat applications or online gaming. Additionally, SSE connections may be subject to browser limitations, and support for SSE varies across different browsers and platforms.

Overall, SSE provides a lightweight and efficient mechanism for delivering real-time updates to web applications. While it may not be suitable for every use case, SSE offers a compelling solution for scenarios where simplicity, reliability, and efficiency are paramount. By understanding the strengths and limitations of SSE, developers can leverage this technology to build engaging and responsive web applications that keep users informed and up-to-date in real-time.

FAQs

  1. What are Server-Sent Events (SSE)?

    Server-Sent Events (SSE) is a web technology that allows servers to push real-time updates to web clients over HTTP. Unlike other real-time communication protocols like WebSockets, SSE is based on a unidirectional flow of data from the server to the client.

  2. How do SSE differ from WebSockets?

    SSE and WebSockets both enable real-time communication between the server and the client, but they have different use cases and implementations. SSE is simpler to set up and is primarily designed for scenarios where data needs to be pushed from the server to the client in real-time without the need for bidirectional communication. WebSockets, on the other hand, support full-duplex communication, allowing data to be sent and received bidirectionally between the server and the client.

  3. Which browsers support Server-Sent Events?

    Server-Sent Events are supported by most modern web browsers, including Google Chrome, Mozilla Firefox, Safari, and Microsoft Edge. However, Internet Explorer does not support SSE. It's important to check browser compatibility when using SSE in web applications.

  4. How do Server-Sent Events handle reconnections and disconnections?

    Server-Sent Events support automatic reconnection in case of network disruptions. When a client loses its connection to the server, it will automatically attempt to reconnect after a certain period of time. Additionally, SSE connections are resilient to short-lived network interruptions and can recover gracefully without requiring manual intervention.

  5. Are there tools to automate the commenting process?

    Server-Sent Events are well-suited for a variety of real-time applications, including live updates of stock prices, sports scores, news feeds, and chat applications. SSE can also be used for monitoring systems, event notifications, and streaming audio or video data to clients in real-time. Overall, SSE is a versatile technology that can enhance the interactivity and responsiveness of web applications in numerous ways.