DEV Community

Ovviamente NO
Ovviamente NO

Posted on

Issues with Socket.io and Express for Project Creation

Hi everyone!

I’m just starting out with Express and Socket.io, and I’m eager to dive into some hands-on projects to get a better grasp of these technologies. I’ve been reading the documentation and tutorials, but I’m having a hard time finding a straightforward project idea to start with.

Could anyone suggest a simple project or idea that would be a good starting point for a beginner? Maybe something that would help me practice integrating Express with Socket.io and build a functional application from scratch?

I’d really appreciate any recommendations or guidance you can offer. Thanks in advance for your help!

Top comments (1)

Collapse
 
alberto_angeli_46003238cd profile image
Alberto Angeli

Hi there!

I just started working on a project using Express and Socket.io and I wanted to share some initial code that might help you get started. It’s a basic auction system with user registration, login, auction creation, bidding, and chat functionality.

Here’s the code for both the server and a simple client-side setup:
**
Server Code (Node.js + Express + Socket.io):**

const express = require('express');
const http = require('http');
const socketIo = require('socket.io');
const crypto = require('crypto');

const app = express();
const server = http.createServer(app);
const io = socketIo(server);

const users = []; // { username, password }
const auctions = [];
const messages = []; // In-memory chat messages

app.use(express.json());
app.use(express.static('public'));

// Generate a unique session ID
const generateSessionId = () => crypto.randomBytes(16).toString('hex');

// Session storage
const sessions = {}; // { sessionId: username }

// Authentication middleware
const authenticateSession = (req, res, next) => {
  const sessionId = req.headers['x-session-id'];
  if (!sessionId || !sessions[sessionId]) return res.sendStatus(401);
  req.user = sessions[sessionId];
  next();
};

// User registration
app.post('/api/register', (req, res) => {
  const { username, password } = req.body;
  if (users.some(u => u.username === username)) {
    return res.status(400).send('User already exists');
  }
  users.push({ username, password });
  res.status(201).send('User registered');
});

// User login
app.post('/api/login', (req, res) => {
  const { username, password } = req.body;
  const user = users.find(u => u.username === username);
  if (user && user.password === password) {
    const sessionId = generateSessionId();
    sessions[sessionId] = username;
    res.json({ sessionId });
  } else {
    res.status(401).send('Invalid credentials');
  }
});

// Create a new auction
app.post('/api/auctions', authenticateSession, (req, res) => {
  const { title, description, startingBid } = req.body;
  const auction = { id: auctions.length + 1, title, description, startingBid, bids: [] };
  auctions.push(auction);
  io.emit('new-auction', auction);
  res.status(201).json(auction);
});

// Get all auctions
app.get('/api/auctions', (req, res) => {
  res.json(auctions);
});

// Place a bid
app.post('/api/auctions/:id/bid', authenticateSession, (req, res) => {
  const auction = auctions.find(a => a.id == req.params.id);
  if (auction) {
    const amount = req.body.amount;
    const highestBid = auction.bids.reduce((max, bid) => bid.amount > max ? bid.amount : max, auction.startingBid);
    if (amount > highestBid) {
      const bid = { user: req.user, amount };
      auction.bids.push(bid);
      io.emit('new-bid', { auctionId: auction.id, bid });
      res.status(201).json(bid);
    } else {
      res.status(400).send('Bid must be higher than the current highest bid');
    }
  } else {
    res.status(404).send('Auction not found');
  }
});

// Delete an auction
app.delete('/api/auctions/:id', authenticateSession, (req, res) => {
  const auctionId = parseInt(req.params.id);
  const auctionIndex = auctions.findIndex(a => a.id === auctionId);
  if (auctionIndex !== -1) {
    auctions.splice(auctionIndex, 1); // Remove auction from array
    io.emit('auction-deleted', auctionId); // Notify clients
    res.status(200).json({ message: 'Auction deleted' });
  } else {
    res.status(404).json({ error: 'Auction not found' });
  }
});

// Socket.io connection handling
io.on('connection', (socket) => {
  console.log('New client connected');
  socket.emit('chat-history', messages);
  socket.on('chat-message', (msg) => {
    const message = { user: msg.user, text: msg.text };
    messages.push(message);
    io.emit('chat-message', message);
  });
  socket.on('disconnect', () => {
    console.log('Client disconnected');
  });
});

server.listen(3000, () => {
  console.log('Server is running on port 3000');
});

Enter fullscreen mode Exit fullscreen mode

Client Code :


<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Online Auction</title>
  <link rel="stylesheet" href="styles.css">
</head>
<body>
  <div class="container">
    <h1>Online Auction</h1>
    <!-- Authentication Section -->
    <div id="auth">
      <div id="login-section">
        <h2>Login</h2>
        <input type="text" id="login-username" placeholder="Username">
        <input type="password" id="login-password" placeholder="Password">
        <button onclick="login()">Login</button>
        <p>Don't have an account? <a href="#" onclick="showRegister()">Register</a></p>
      </div>
      <div id="register-section" style="display:none;">
        <h2>Register</h2>
        <input type="text" id="register-username" placeholder="Username">
        <input type="password" id="register-password" placeholder="Password">
        <button onclick="register()">Register</button>
        <p>Already have an account? <a href="#" onclick="showLogin()">Login</a></p>
      </div>
    </div>
    <!-- Auction Section -->
    <div id="auction" style="display:none;">
      <h2>Create Auction</h2>
      <input type="text" id="auction-title" placeholder="Title">
      <input type="text" id="auction-description" placeholder="Description">
      <input type="number" id="auction-startingBid" placeholder="Starting Bid">
      <button onclick="createAuction()">Create Auction</button>
      <h2>Auctions</h2>
      <div id="auctions"></div>
      <h2>Auction Summary</h2>
      <div id="auction-summary" style="display:none;">
        <h3 id="summary-title"></h3>
        <p id="summary-description"></p>
        <p id="summary-startingBid"></p>
        <div id="summary-bids"></div>
        <input type="number" id="bid-amount" placeholder="Enter your bid">
        <button onclick="placeBid()">Place Bid</button>
      </div>
      <h2>Chat</h2>
      <div id="chat">
        <div id="messages"></div>
        <input type="text" id="chat-input" placeholder="Type a message">
        <button onclick="sendMessage()">Send</button>
      </div>
    </div>
  </div>
  <script src="/socket.io/socket.io.js"></script>
  <script>
    const socket = io();
    let sessionId = '';
    let username = '';
    let selectedAuctionId = null;


    function showRegister() {
      document.getElementById('login-section').style.display = 'none';
      document.getElementById('register-section').style.display = 'block';
    }


    function showLogin() {
      document.getElementById('register-section').style.display = 'none';
      document.getElementById('login-section').style.display = 'block';
    }


    function register() {
      const usernameInput = document.getElementById('register-username').value;
      const password = document.getElementById('register-password').value;


      fetch('/api/register', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ username: usernameInput, password })
      }).then(response => {
        if (response.ok) {
          alert('User registered');
          showLogin();
        } else {
          alert('Registration failed');
        }
      });
    }


    function login() {
      const usernameInput = document.getElementById('login-username').value;
      const password = document.getElementById('login-password').value;


      fetch('/api/login', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ username: usernameInput, password })
      }).then(response => response.json())
        .then(data => {
          if (data.sessionId) {
            sessionId = data.sessionId;
            username = usernameInput;
            document.getElementById('auth').style.display = 'none';
            document.getElementById('auction').style.display = 'block';
            loadAuctions();
          } else {
            alert('Login failed');
          }
        });
    }


    function createAuction() {
      const title = document.getElementById('auction-title').value;
      const description = document.getElementById('auction-description').value;
      const startingBid = document.getElementById('auction-startingBid').value;


      fetch('/api/auctions', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          'X-Session-Id': sessionId
        },
        body: JSON.stringify({ title, description, startingBid })
      }).then(response => response.json())
        .then(auction => {
          alert('Auction created');
          loadAuctions();
        });
    }


    function loadAuctions() {
      fetch('/api/auctions')
        .then(response => response.json())
        .then(auctions => {
          const auctionsDiv = document.getElementById('auctions');
          auctionsDiv.innerHTML = '';
          auctions.forEach(auction => {
            const auctionDiv = document.createElement('div');
            auctionDiv.classList.add('auction-item');
            auctionDiv.innerHTML = `
              <h3>${auction.title}</h3>
              <p>${auction.description}</p>
              <p>Starting Bid: ${auction.startingBid}</p>
              <button onclick="deleteAuction(${auction.id})">Delete</button>
            `;
            auctionDiv.onclick = () => handleAuctionClick(auction);
            auctionsDiv.appendChild(auctionDiv);
          });
        });
    }


    function handleAuctionClick(auction) {
      if (selectedAuctionId === auction.id) {
        document.getElementById('auction-summary').style.display = 'none';
        selectedAuctionId = null;
      } else {
        showAuctionSummary(auction);
      }
    }


    function showAuctionSummary(auction) {
      selectedAuctionId = auction.id;
      document.getElementById('summary-title').textContent = auction.title;
      document.getElementById('summary-description').textContent = auction.description;
      document.getElementById('summary-startingBid').textContent = `Starting Bid: ${auction.startingBid}`;
      loadBids(auction.id);
      document.getElementById('auction-summary').style.display = 'block';
    }


    function loadBids(auctionId) {
      fetch(`/api/auctions`)
        .then(response => response.json())
        .then(auctions => {
          const auction = auctions.find(a => a.id == auctionId);
          const bidsDiv = document.getElementById('summary-bids');
          bidsDiv.innerHTML = '';
          if (auction) {
            auction.bids.forEach(bid => {
              const bidDiv = document.createElement('div');
              bidDiv.classList.add('bid-item');
              bidDiv.innerHTML = `<p>${bid.user}: ${bid.amount}</p>`;
              bidsDiv.appendChild(bidDiv);
            });
          }
        });
    }


    function placeBid() {
      const amount = document.getElementById('bid-amount').value;
      fetch(`/api/auctions/${selectedAuctionId}/bid`, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          'X-Session-Id': sessionId
        },
        body: JSON.stringify({ amount })
      }).then(response => {
        if (response.ok) {
          return response.json();
        } else {
          return response.text().then(text => { throw new Error(text); });
        }
      }).then(bid => {
        alert('Bid placed');
        loadBids(selectedAuctionId);
      }).catch(error => {
        alert('Failed to place bid: ' + error.message);
      });
    }


    function deleteAuction(id) {
      if (confirm('Are you sure you want to delete this auction?')) {
        fetch(`/api/auctions/${id}`, {
          method: 'DELETE',
          headers: {
            'Content-Type': 'application/json',
            'X-Session-Id': sessionId
          }
        }).then(response => {
          if (response.ok) {
            return response.json();
          } else {
            return response.text().then(text => { throw new Error(text); });
          }
        }).then(data => {
          alert('Auction deleted');
          selectedAuctionId = null;
          document.getElementById('auction-summary').style.display = 'none';
          loadAuctions();
        }).catch(error => {
          alert('Failed to delete auction: ' + error.message);
        });
      }
    }


    function sendMessage() {
      const input = document.getElementById('chat-input');
      const message = input.value;
      socket.emit('chat-message', { user: username, text: message });
      input.value = '';
    }


    socket.on('chat-history', (messages) => {
      const messagesDiv = document.getElementById('messages');
      messages.forEach(msg => {
        const messageDiv = document.createElement('div');
        messageDiv.classList.add('message-item');
        messageDiv.textContent = `${msg.user}: ${msg.text}`;
        messagesDiv.appendChild(messageDiv);
      });
    });


    socket.on('chat-message', (msg) => {
      const messagesDiv = document.getElementById('messages');
      const messageDiv = document.createElement('div');
      messageDiv.classList.add('message-item');
      messageDiv.textContent = `${msg.user}: ${msg.text}`;
      messagesDiv.appendChild(messageDiv);
    });


    socket.on('new-auction', (auction) => {
      loadAuctions();
    });


    socket.on('auction-deleted', (auctionId) => {
      const auctionsDiv = document.getElementById('auctions');
      const auctionDivs = auctionsDiv.getElementsByClassName('auction-item');
      for (const auctionDiv of auctionDivs) {
        if (auctionDiv.querySelector('button') && auctionDiv.querySelector('button').onclick.toString().includes(auctionId)) {
          auctionsDiv.removeChild(auctionDiv);
          break;
        }
      }
      if (selectedAuctionId === auctionId) {
        document.getElementById('auction-summary').style.display = 'none';
        selectedAuctionId = null;
      }
    });


    socket.on('new-bid', ({ auctionId, bid }) => {
      if (auctionId === selectedAuctionId) {
        loadBids(auctionId);
      }
    });
  </script>
</body>
</html>

Enter fullscreen mode Exit fullscreen mode

And if you want, a little style for the code!

body {
  font-family: Arial, sans-serif;
}

.container {
  width: 80%;
  margin: auto;
  padding: 20px;
}

h1 {
  text-align: center;
}

#auth, #auction {
  margin-top: 20px;
}

#auth input, #auction input {
  display: block;
  margin: 10px 0;
  padding: 10px;
  width: 100%;
}

button {
  padding: 10px 20px;
  background-color: #007bff;
  color: white;
  border: none;
  cursor: pointer;
}

button:hover {
  background-color: #0056b3;
}

#auctions .auction-item, #summary-bids .bid-item, .message-item {
  border: 1px solid #ddd;
  padding: 10px;
  margin: 10px 0;
}

Enter fullscreen mode Exit fullscreen mode