DEV Community

Ravi Bhuvan
Ravi Bhuvan

Posted on Edited on

Project Explanation for my Chesso application

Project

High-Level Summary

Chesso is a real-time multiplayer chess platform that I built using React, Node.js, Express, Socket.IO, MongoDB and Chess.js.

The main goal was to provide a synchronized multiplayer experience where two players can play the same game in real time. The React frontend handles the chess board and user interactions, while the Node.js and Express backend manages APIs and game-related logic.

For real-time communication, I used Socket.IO. When a player makes a move, the move is sent to the server, where I validate it using Chess.js rather than trusting the client. The server then updates the authoritative game state and broadcasts the updated state to the other player.

For authentication, I integrated Google OAuth 2.0 and JWT-based sessions, with authentication information persisted using httpOnly cookies. MongoDB is used for persistent user and game-related data.

I also implemented server-authoritative timers and handled conditions such as checkmate, timeout and resignation. Another challenge was reconnection, so I added state-recovery logic so players could continue their game after temporarily losing their connection.

Tech Stack & Architectural Overview

  • Frontend : React, Vite for fast builds, React for declarative UI updates upon WebSocket events.
  • Backend API : Node.js, Express.js Event-driven, non-blocking I/O ideal for handling multiple concurrent WebSocket connections.
  • Socket.IO : Provides bi-directional socket events, auto-reconnection fallback, and socket room abstraction.
  • Database : MongoDB for flexible JSON-like document model ideal for storing FEN strings, match logs, and user metadata.
  • Auth & Security : Google OAuth 2.0, Passport.js, JWT, bcryptusing standard authentication flow providing password hashing (bcrypt) and session security via JWT tokens.
  1. Implementation of matchmaking & Queueing System

    • When a player clicks "Play", the client emits StartGame with their playerID.
    • The server verifies turn ownership (game.currentP === playerID).
    • The move is executed in an isolated server-side Chess() instance (gameSockets.js).
    • If empty, the player is queued and notified via waitingForOpponent.
    • If another player is waiting, waitingQ.shift() pairs them instantly, creates a new game record in MongoDB (GameModel.js), assigns piece colors (white/black), and joins both sockets into a dedicated Socket.IO room named after the gameID.
  2. Game Recovery & Reconnection Resilience

    • The server exposes a recoverGame socket event (gameSockets.js).
    • If an active game exists in Node's memory, the socket re-joins the room and emits recoverGameState.
    • If the Node instance restarted, the state is hydrated dynamically from MongoDB using the saved board FEN string (new Chess(DbGame.boardState)).
  3. What was the hardest technical challenge in this project?

    • Handling chess timers in real-time requires strict concurrency control. If timers ran strictly on the client, users could tamper with local timers
    • I solved this by building backend-driven interval timers per active room. Every time a move occurs, the active timer stops, the remaining time is updated in memory and DB, and the next player's timer fires while broadcasting synced timestamp snapshots to both clients.
  4. Why did you use Socket.IO instead of traditional HTTP REST APIs or pure WebSockets?

  • For turn-based real-time games like chess, HTTP polling incurs high latency and unnecessary overhead because HTTP headers are sent with every request. Socket.IO provides full-duplex, persistent TCP connections for bi-directional streaming.
  • I chose Socket.IO over raw WebSockets because Socket.IO provides out-of-the-box abstractions like Rooms (allowing two players to be isolated into a single gameID room), automatic reconnection fallback mechanisms (polling if WebSockets are blocked by proxies), and simple event-based broadcasting.
  1. Walk me through your database schemas.
  • User Schema (UserModels.js): Stores user credentials (Name, email, password). It includes a pre-save hook using bcrypt to hash passwords automatically, and custom methods like comparepassword() and generateAccessToken() for JWT authentication.
  • Game Schema (GameModel.js): References player1 and player2 via Mongoose ObjectId references to User. It tracks boardState (FEN string), currentP, status ('waiting', 'ongoing', 'finished'), WinnerID, Result ('Time-Out', 'Resignation', 'CheckMate', 'Draw'), and the remaining timer snapshot.
  1. How would you scale Chesso to handle 100,000 active concurrent chess matches?
  • Stateless Backend Nodes + Redis Adapter: Spin up multiple Node.js socket servers behind an NGINX / AWS ALB load balancer. Connect all socket nodes using the @socket.io/redis-adapter so sockets across different servers can communicate.
  • Distributed In-Memory Store (Redis): Move active game states and timer counters into Redis Cache. Redis data structures (e.g., Hashes) allow sub-millisecond atomic reads/writes for game state updates.
  1. What is OAuth 2.0, how the end-to-end flow works?
  • At a high level, OAuth 2.0 is a secure delegation protocol that allows users to authenticate using a trusted third-party provider like Google without exposing their password to our application. In Chesso, I implemented the Authorization Code Grant Flow with Passport.js, bridged with an internal JWT authentication system.
    • First, when a user clicks 'Sign in with Google', the frontend hits my backend endpoint at /auth/google (AuthRoutes.js). This triggers passport. authenticate('google') which redirects the user’s browser to Google’s OAuth 2.0 consent screen, requesting the profile and email scopes.
    • Second, once the user approves, Google redirects back to my registered backend callback URL at /auth/google/callback with a one-time Authorization Code.
    • Third, behind the scenes inside passport.js, Passport automatically exchanges that code with Google's servers for the user's profile data (email, displayName, photo). My callback strategy then checks MongoDB for an existing user record. If it’s their first time logging in, I automatically upsert a new User document with their Google details.
    • Fourth—and this is a key architectural decision— instead of passing Google’s token back to the frontend, my controller in AuthControllers.js generates our own custom, short-lived JWT access token via user.generateAccessToken().
  1. how did u come with a System Design for this project?

When designing Chesso, my primary goal was to build an ultra-low-latency, server-authoritative system that prevents client-side cheating while handling disconnects gracefully

  • Protocol & State Representation
    • Standard HTTP polling introduces too much overhead for turn-based games, so I chose Socket.IO for full-duplex WebSocket communication. To keep payload sizes lightweight (~60 bytes), I represented the full board state using FEN strings (Forsyth–Edwards Notation) rather than sending heavy move arrays.
  • Dual-Layer Storage Architecture
    • Active games and backend Node.js setInterval clocks live in memory for sub-millisecond move processing and timer synchronization and Every valid move updates a persistent record in MongoDB. If a user refreshes or a server restarts, the recoverGame socket event.
  • Server-Authoritative Logic
    • All move validations and game clocks run strictly on the backend via chess.js and Node intervals. The client is purely a view layer, eliminating local timer drift or move manipulation.

9.What would happen if Player 2 disconnects in the middle of a game? How did you handle reconnection?

To handle disconnections, the game state remains on the server while the player's clock continues running. When the player reconnects, the frontend sends a recoverGame request with the game ID and credentials. The server verifies the player, reconnects them to the game room, and sends the latest game state, including the board position, turn, and remaining time, so they can continue without losing progress.

Top comments (0)